diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8266c03eaa72..f539b64d8c82 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,12 +117,12 @@ jobs: with: fetch-depth: 2 - # Free up disk space on Linux and Windows by removing preinstalled components that + # Free up disk space on Linux by removing preinstalled components that # we do not need. We do this to enable some of the less resource # intensive jobs to run on free runners, which however also have # less disk space. - name: free up disk space - run: src/ci/scripts/free-disk-space.sh + run: src/ci/scripts/free-disk-space-linux.sh if: matrix.free_disk # If we don't need to free up disk space then just report how much space we have @@ -223,11 +223,6 @@ jobs: cd src/ci/citool CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=../../../build/citool cargo build - - name: wait for Windows disk cleanup to finish - if: ${{ matrix.free_disk && startsWith(matrix.os, 'windows-') }} - run: | - python3 src/ci/scripts/free-disk-space-windows-wait.py - - name: run the build run: | set +e diff --git a/Cargo.lock b/Cargo.lock index 85df0dda8185..af206e87c452 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,9 +152,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "ar_archive_writer" @@ -204,7 +204,7 @@ dependencies = [ "rustc-hash 2.1.1", "serde", "serde_derive", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" [[package]] name = "blake3" @@ -315,7 +315,7 @@ dependencies = [ "serde_json", "sha2", "tar", - "toml 0.5.11", + "toml 0.7.8", "xz2", ] @@ -336,7 +336,7 @@ dependencies = [ "curl", "indexmap", "serde", - "toml 0.5.11", + "toml 0.7.8", ] [[package]] @@ -421,7 +421,7 @@ dependencies = [ "serde", "serde-untagged", "serde-value", - "thiserror 2.0.12", + "thiserror 2.0.15", "toml 0.8.23", "unicode-xid", "url", @@ -453,7 +453,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -518,9 +518,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.43" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", "clap_derive", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.43" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -550,14 +550,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -720,7 +720,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -907,9 +907,9 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.48" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2d5c8f48d9c0c23250e52b55e82a6ab4fdba6650c931f5a0a57a43abda812b" +checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc" dependencies = [ "curl-sys", "libc", @@ -922,9 +922,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.82+curl-8.14.1" +version = "0.4.83+curl-8.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4d63638b5ec65f1a4ae945287b3fd035be4554bbaf211901159c9a2a74fb5be" +checksum = "5830daf304027db10c82632a464879d46a3f7c4ba17a31592657ad16c719b483" dependencies = [ "cc", "libc", @@ -937,9 +937,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.166" +version = "1.0.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5287274dfdf7e7eaa3d97d460eb2a94922539e6af214bda423f292105011ee2" +checksum = "7aa144b12f11741f0dab5b4182896afad46faa0598b6a061f7b9d17a21837ba7" dependencies = [ "cc", "cxxbridge-cmd", @@ -951,9 +951,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.166" +version = "1.0.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f3ce027a744135db10a1ebffa0863dab685aeef48f40a02c201f5e70c667d3" +checksum = "12d3cbb84fb003242941c231b45ca9417e786e66e94baa39584bd99df3a270b6" dependencies = [ "cc", "codespan-reporting", @@ -961,40 +961,40 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.166" +version = "1.0.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07dc23f2eea4774297f4c9a17ae4065fecb63127da556e6c9fadb0216d93595" +checksum = "3fa36b7b249d43f67a3f54bd65788e35e7afe64bbc671396387a48b3e8aaea94" dependencies = [ "clap", "codespan-reporting", "indexmap", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "cxxbridge-flags" -version = "1.0.166" +version = "1.0.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7a4dbad6171f763c4066c83dcd27546b6e93c5c5ae2229f9813bda7233f571d" +checksum = "77707c70f6563edc5429618ca34a07241b75ebab35bd01d46697c75d58f8ddfe" [[package]] name = "cxxbridge-macro" -version = "1.0.166" +version = "1.0.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9be4b527950fc42db06163705e78e73eedc8fd723708e942afe3572a9a2c366" +checksum = "ede6c0fb7e318f0a11799b86ee29dcf17b9be2960bd379a6c38e1a96a6010fff" dependencies = [ "indexmap", "proc-macro2", "quote", "rustversion", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1018,7 +1018,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1029,7 +1029,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1061,7 +1061,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1082,7 +1082,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1092,7 +1092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1104,7 +1104,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1129,16 +1129,16 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "dirs-sys 0.5.0", + "dirs-sys", ] [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys", ] [[package]] @@ -1151,18 +1151,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.6", - "windows-sys 0.48.0", -] - [[package]] name = "dirs-sys" version = "0.5.0" @@ -1194,7 +1182,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1379,7 +1367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" dependencies = [ "memchr", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -1539,9 +1527,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "globset" @@ -1855,7 +1843,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2044,7 +2032,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2102,7 +2090,7 @@ dependencies = [ "pest_derive", "regex", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -2337,7 +2325,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2643,9 +2631,9 @@ dependencies = [ [[package]] name = "object" -version = "0.37.2" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e3d0a7419f081f4a808147e845310313a39f322d7ae1f996b7f001d6cbed04" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "crc32fast", "flate2", @@ -2653,7 +2641,7 @@ dependencies = [ "indexmap", "memchr", "ruzstd 0.8.1", - "wasmparser 0.236.0", + "wasmparser 0.236.1", ] [[package]] @@ -2834,7 +2822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", - "thiserror 2.0.12", + "thiserror 2.0.15", "ucd-trie", ] @@ -2858,7 +2846,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3007,9 +2995,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -3147,9 +3135,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -3157,9 +3145,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -3193,7 +3181,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -3279,11 +3267,11 @@ dependencies = [ "build_helper", "gimli 0.32.0", "libc", - "object 0.37.2", + "object 0.37.3", "regex", "serde_json", "similar", - "wasmparser 0.236.0", + "wasmparser 0.236.1", ] [[package]] @@ -3367,6 +3355,7 @@ dependencies = [ "rand 0.9.2", "rand_xoshiro", "rustc_data_structures", + "rustc_error_messages", "rustc_hashes", "rustc_index", "rustc_macros", @@ -3458,7 +3447,6 @@ dependencies = [ "rustc_feature", "rustc_fluent_macro", "rustc_macros", - "rustc_parse", "rustc_session", "rustc_span", "rustc_target", @@ -3480,7 +3468,6 @@ dependencies = [ name = "rustc_attr_parsing" version = "0.0.0" dependencies = [ - "itertools", "rustc_abi", "rustc_ast", "rustc_ast_pretty", @@ -3490,6 +3477,7 @@ dependencies = [ "rustc_hir", "rustc_lexer", "rustc_macros", + "rustc_parse", "rustc_session", "rustc_span", "thin-vec", @@ -3570,7 +3558,7 @@ dependencies = [ "itertools", "libc", "measureme", - "object 0.37.2", + "object 0.37.3", "rustc-demangle", "rustc_abi", "rustc_ast", @@ -3608,7 +3596,7 @@ dependencies = [ "cc", "itertools", "libc", - "object 0.37.2", + "object 0.37.3", "pathdiff", "regex", "rustc_abi", @@ -3779,6 +3767,8 @@ dependencies = [ "icu_locid", "icu_provider_adapters", "intl-memoizer", + "rustc_ast", + "rustc_ast_pretty", "rustc_baked_icu_data", "rustc_data_structures", "rustc_macros", @@ -3796,21 +3786,17 @@ dependencies = [ "derive_setters", "rustc_abi", "rustc_ast", - "rustc_ast_pretty", "rustc_data_structures", "rustc_error_codes", "rustc_error_messages", "rustc_fluent_macro", "rustc_hashes", - "rustc_hir", "rustc_index", "rustc_lexer", "rustc_lint_defs", "rustc_macros", "rustc_serialize", "rustc_span", - "rustc_target", - "rustc_type_ir", "serde", "serde_json", "termcolor", @@ -3865,7 +3851,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "unic-langid", ] @@ -3898,7 +3884,9 @@ dependencies = [ "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", + "rustc_error_messages", "rustc_hashes", + "rustc_hir_id", "rustc_index", "rustc_macros", "rustc_serialize", @@ -3936,6 +3924,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "rustc_hir_id" +version = "0.0.0" +dependencies = [ + "rustc_data_structures", + "rustc_index", + "rustc_macros", + "rustc_serialize", + "rustc_span", +] + [[package]] name = "rustc_hir_pretty" version = "0.0.0" @@ -4012,7 +4011,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4123,11 +4122,10 @@ dependencies = [ name = "rustc_lint_defs" version = "0.0.0" dependencies = [ - "rustc_abi", "rustc_ast", "rustc_data_structures", "rustc_error_messages", - "rustc_hir", + "rustc_hir_id", "rustc_macros", "rustc_serialize", "rustc_span", @@ -4158,7 +4156,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -4268,7 +4266,6 @@ dependencies = [ "rustc_errors", "rustc_fluent_macro", "rustc_graphviz", - "rustc_hir", "rustc_index", "rustc_macros", "rustc_middle", @@ -4345,7 +4342,6 @@ dependencies = [ "rustc-literal-escaper", "rustc_ast", "rustc_ast_pretty", - "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -4644,9 +4640,10 @@ name = "rustc_target" version = "0.0.0" dependencies = [ "bitflags", - "object 0.37.2", + "object 0.37.3", "rustc_abi", "rustc_data_structures", + "rustc_error_messages", "rustc_fs_util", "rustc_macros", "rustc_serialize", @@ -4710,7 +4707,6 @@ name = "rustc_traits" version = "0.0.0" dependencies = [ "rustc_data_structures", - "rustc_hir", "rustc_infer", "rustc_middle", "rustc_span", @@ -4765,6 +4761,7 @@ dependencies = [ "rustc-hash 2.1.1", "rustc_ast_ir", "rustc_data_structures", + "rustc_error_messages", "rustc_index", "rustc_macros", "rustc_serialize", @@ -4781,7 +4778,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -4812,6 +4809,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", + "stringdex", "tempfile", "threadpool", "tracing", @@ -4872,7 +4870,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5009,9 +5007,9 @@ dependencies = [ [[package]] name = "serde-untagged" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" +checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3" dependencies = [ "erased-serde", "serde", @@ -5036,7 +5034,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5127,12 +5125,12 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5225,6 +5223,15 @@ dependencies = [ "quote", ] +[[package]] +name = "stringdex" +version = "0.0.1-alpha4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0" +dependencies = [ + "stacker", +] + [[package]] name = "strsim" version = "0.11.1" @@ -5263,9 +5270,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -5280,7 +5287,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5398,11 +5405,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.15", ] [[package]] @@ -5413,18 +5420,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5525,15 +5532,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "toml" version = "0.7.8" @@ -5621,7 +5619,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5804,7 +5802,7 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.104", + "syn 2.0.106", "unic-langid-impl", ] @@ -5936,9 +5934,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -6016,7 +6014,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -6038,7 +6036,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6101,12 +6099,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.236.0" +version = "0.236.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3108979166ab0d3c7262d2e16a2190ffe784b2a5beb963edef154b5e8e07680b" +checksum = "724fccfd4f3c24b7e589d333fc0429c68042897a7e8a5f8694f31792471841e7" dependencies = [ "leb128fmt", - "wasmparser 0.236.0", + "wasmparser 0.236.1", ] [[package]] @@ -6146,9 +6144,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.236.0" +version = "0.236.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d1eee846a705f6f3cb9d7b9f79b54583810f1fb57a1e3aea76d1742db2e3d2" +checksum = "a9b1e81f3eb254cf7404a82cee6926a4a3ccc5aad80cc3d43608a070c67aa1d7" dependencies = [ "bitflags", "indexmap", @@ -6157,22 +6155,22 @@ dependencies = [ [[package]] name = "wast" -version = "236.0.0" +version = "236.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d6b6faeab519ba6fbf9b26add41617ca6f5553f99ebc33d876e591d2f4f3c6" +checksum = "d3bec4b4db9c6808d394632fd4b0cd4654c32c540bd3237f55ee6a40fff6e51f" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.1", - "wasm-encoder 0.236.0", + "wasm-encoder 0.236.1", ] [[package]] name = "wat" -version = "1.236.0" +version = "1.236.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc31704322400f461f7f31a5f9190d5488aaeafb63ae69ad2b5888d2704dcb08" +checksum = "64475e2f77d6071ce90624098fc236285ddafa8c3ea1fb386f2c4154b6c2bbdb" dependencies = [ "wast", ] @@ -6306,7 +6304,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6317,7 +6315,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6328,7 +6326,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6339,7 +6337,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6772,7 +6770,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -6784,7 +6782,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -6805,7 +6803,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6825,7 +6823,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -6870,7 +6868,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6881,5 +6879,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] diff --git a/RELEASES.md b/RELEASES.md index b6dc06286467..33abe45ce462 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1778,7 +1778,7 @@ Language - [Undeprecate lint `unstable_features` and make use of it in the compiler.](https://github.com/rust-lang/rust/pull/118639/) - [Make inductive cycles in coherence ambiguous always.](https://github.com/rust-lang/rust/pull/118649/) - [Get rid of type-driven traversal in const-eval interning](https://github.com/rust-lang/rust/pull/119044/), - only as a [future compatiblity lint](https://github.com/rust-lang/rust/pull/122204) for now. + only as a [future compatibility lint](https://github.com/rust-lang/rust/pull/122204) for now. - [Deny braced macro invocations in let-else.](https://github.com/rust-lang/rust/pull/119062/) diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 5f9afc46a1ac..83d96d8d04da 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -9,6 +9,7 @@ bitflags = "2.4.1" rand = { version = "0.9.0", default-features = false, optional = true } rand_xoshiro = { version = "0.7.0", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } +rustc_error_messages = { path = "../rustc_error_messages", optional = true } rustc_hashes = { path = "../rustc_hashes" } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } @@ -24,6 +25,7 @@ default = ["nightly", "randomize"] # without depending on rustc_data_structures, rustc_macros and rustc_serialize nightly = [ "dep:rustc_data_structures", + "dep:rustc_error_messages", "dep:rustc_macros", "dep:rustc_serialize", "dep:rustc_span", diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 29a3678abf3f..41d744e1946a 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -223,6 +223,9 @@ impl StableOrd for ExternAbi { const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } +#[cfg(feature = "nightly")] +rustc_error_messages::into_diag_arg_using_display!(ExternAbi); + impl ExternAbi { /// An ABI "like Rust" /// diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 87c9c797ea5b..de3e0e0c87f5 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3137,7 +3137,7 @@ pub fn span(&self) -> Span { #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, Walkable)] pub enum Inline { Yes, - No, + No { had_parse_error: Result<(), ErrorGuaranteed> }, } /// Module item kind. @@ -3147,7 +3147,7 @@ pub enum ModKind { /// or with definition outlined to a separate file `mod foo;` and already loaded from it. /// The inner span is from the first token past `{` to the last token until `}`, /// or from the first to the last token in the loaded file. - Loaded(ThinVec>, Inline, ModSpans, Result<(), ErrorGuaranteed>), + Loaded(ThinVec>, Inline, ModSpans), /// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it. Unloaded, } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index fc816f2cb792..ea98bebd3055 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -7,6 +7,7 @@ pub use TokenKind::*; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; +use rustc_span::symbol::IdentPrintMode; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym}; #[allow(clippy::useless_attribute)] // FIXME: following use of `hidden_glob_reexports` incorrectly triggers `useless_attribute` lint. #[allow(hidden_glob_reexports)] @@ -344,15 +345,24 @@ pub enum IdentIsRaw { Yes, } -impl From for IdentIsRaw { - fn from(b: bool) -> Self { - if b { Self::Yes } else { Self::No } +impl IdentIsRaw { + pub fn to_print_mode_ident(self) -> IdentPrintMode { + match self { + IdentIsRaw::No => IdentPrintMode::Normal, + IdentIsRaw::Yes => IdentPrintMode::RawIdent, + } + } + pub fn to_print_mode_lifetime(self) -> IdentPrintMode { + match self { + IdentIsRaw::No => IdentPrintMode::Normal, + IdentIsRaw::Yes => IdentPrintMode::RawLifetime, + } } } -impl From for bool { - fn from(is_raw: IdentIsRaw) -> bool { - matches!(is_raw, IdentIsRaw::Yes) +impl From for IdentIsRaw { + fn from(b: bool) -> Self { + if b { Self::Yes } else { Self::No } } } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index e55399adfb85..f4f35a4d2ee0 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -907,6 +907,12 @@ pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { pub fn bump(&mut self) { self.index += 1; } + + // For skipping ahead in rare circumstances. + #[inline] + pub fn bump_to_end(&mut self) { + self.index = self.stream.len(); + } } /// A `TokenStream` cursor that produces `Token`s. It's a bit odd that diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 72817a0a9a05..bb559bd89212 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -251,7 +251,7 @@ fn lower_item_kind( ItemKind::Mod(_, ident, mod_kind) => { let ident = self.lower_ident(*ident); match mod_kind { - ModKind::Loaded(items, _, spans, _) => { + ModKind::Loaded(items, _, spans) => { hir::ItemKind::Mod(ident, self.lower_mod(items, spans)) } ModKind::Unloaded => panic!("`mod` items should have been loaded by now"), @@ -1596,7 +1596,7 @@ pub(super) fn lower_fn_header( let safety = self.lower_safety(h.safety, default_safety); // Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so. - let safety = if find_attr!(attrs, AttributeKind::TargetFeature { .. }) + let safety = if find_attr!(attrs, AttributeKind::TargetFeature { was_forced: false, .. }) && safety.is_safe() && !self.tcx.sess.target.is_like_wasm { diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index 1940628b44a5..3e04f8b11ec9 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -15,7 +15,6 @@ rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_macros = { path = "../rustc_macros" } -rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 73cbcdd30ced..a95f14439684 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -20,6 +20,10 @@ ast_passes_abi_must_not_have_return_type= .note = functions with the {$abi} ABI cannot have a return type .help = remove the return type +ast_passes_abi_x86_interrupt = + invalid signature for `extern "x86-interrupt"` function + .note = functions with the "x86-interrupt" ABI must be have either 1 or 2 parameters (but found {$param_count}) + ast_passes_assoc_const_without_body = associated constant in `impl` without body .suggestion = provide a definition for the constant @@ -109,6 +113,10 @@ ast_passes_extern_without_abi = `extern` declarations without an explicit ABI ar .suggestion = specify an ABI .help = prior to Rust 2024, a default ABI was inferred +ast_passes_extern_without_abi_sugg = `extern` declarations without an explicit ABI are deprecated + .label = ABI should be specified here + .suggestion = explicitly specify the {$default_abi} ABI + 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 diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 0c72f3190074..6133cc3548b5 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -25,16 +25,16 @@ use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list}; use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; +use rustc_attr_parsing::validate_attr; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxtHandle, LintBuffer}; use rustc_feature::Features; -use rustc_parse::validate_attr; use rustc_session::Session; +use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, PATTERNS_IN_FNS_WITHOUT_BODY, }; -use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_span::{Ident, Span, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use thin_vec::thin_vec; @@ -405,6 +405,17 @@ fn check_extern_fn_signature(&self, abi: ExternAbi, ctxt: FnCtxt, ident: &Ident, if let InterruptKind::X86 = interrupt_kind { // "x86-interrupt" is special because it does have arguments. // FIXME(workingjubilee): properly lint on acceptable input types. + let inputs = &sig.decl.inputs; + let param_count = inputs.len(); + if !matches!(param_count, 1 | 2) { + let mut spans: Vec = + inputs.iter().map(|arg| arg.span).collect(); + if spans.is_empty() { + spans = vec![sig.span]; + } + self.dcx().emit_err(errors::AbiX86Interrupt { spans, param_count }); + } + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output && match &ret_ty.kind { TyKind::Never => false, @@ -865,7 +876,7 @@ fn handle_missing_abi(&mut self, span: Span, id: NodeId) { MISSING_ABI, id, span, - BuiltinLintDiag::MissingAbi(span, ExternAbi::FALLBACK), + errors::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK }, ) } } @@ -1169,7 +1180,7 @@ fn visit_item(&mut self, item: &'a Item) { self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" }); } // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584). - if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) + if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) && !attr::contains_name(&item.attrs, sym::path) { self.check_mod_file_item_asciionly(*ident); diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 5ecc0d21411d..ae8f056cb4e6 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -4,7 +4,7 @@ use rustc_ast::ParamKindOrd; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic}; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; @@ -815,6 +815,14 @@ pub(crate) struct MissingAbi { pub span: Span, } +#[derive(LintDiagnostic)] +#[diag(ast_passes_extern_without_abi_sugg)] +pub(crate) struct MissingAbiSugg { + #[suggestion(code = "extern {default_abi}", applicability = "machine-applicable")] + pub span: Span, + pub default_abi: ExternAbi, +} + #[derive(Diagnostic)] #[diag(ast_passes_abi_custom_safe_foreign_function)] pub(crate) struct AbiCustomSafeForeignFunction { @@ -891,3 +899,12 @@ pub(crate) struct AbiMustNotHaveReturnType { pub span: Span, pub abi: ExternAbi, } + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_x86_interrupt)] +#[note] +pub(crate) struct AbiX86Interrupt { + #[primary_span] + pub spans: Vec, + pub param_count: usize, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index c9344a76a7b3..e763c9d69fc2 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -524,6 +524,7 @@ macro_rules! gate_all { gate_all!(where_clause_attrs, "attributes in `where` clause are unstable"); gate_all!(super_let, "`super let` is experimental"); gate_all!(frontmatter, "frontmatters are experimental"); + gate_all!(coroutines, "coroutine syntax is experimental"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 85f76036df72..a056ce3e29d2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use rustc_ast::attr::AttrIdGenerator; -use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; +use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; @@ -441,7 +441,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool); fn print_ident(&mut self, ident: Ident) { - self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string()); + self.word(IdentPrinter::for_ast_ident(ident, ident.guess_print_mode()).to_string()); self.ann_post(ident) } @@ -1015,17 +1015,16 @@ fn token_kind_to_string_ext( /* Name components */ token::Ident(name, is_raw) => { - IdentPrinter::new(name, is_raw.into(), convert_dollar_crate).to_string().into() + IdentPrinter::new(name, is_raw.to_print_mode_ident(), convert_dollar_crate) + .to_string() + .into() } token::NtIdent(ident, is_raw) => { - IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into() + IdentPrinter::for_ast_ident(ident, is_raw.to_print_mode_ident()).to_string().into() } - token::Lifetime(name, IdentIsRaw::No) - | token::NtLifetime(Ident { name, .. }, IdentIsRaw::No) => name.to_string().into(), - token::Lifetime(name, IdentIsRaw::Yes) - | token::NtLifetime(Ident { name, .. }, IdentIsRaw::Yes) => { - format!("'r#{}", &name.as_str()[1..]).into() + token::Lifetime(name, is_raw) | token::NtLifetime(Ident { name, .. }, is_raw) => { + IdentPrinter::new(name, is_raw.to_print_mode_lifetime(), None).to_string().into() } /* Other */ diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index bac89373b678..fd8f7ffb2ed6 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -5,7 +5,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } @@ -15,6 +14,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } +rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } thin-vec = "0.2.12" diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 4fb66a816522..40e9d597530b 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -12,9 +12,11 @@ attr_parsing_empty_attribute = attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} .help = `#[{$name}]` can {$only}be applied to {$applied} + .suggestion = remove the attribute attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target} .warn = {-attr_parsing_previously_accepted} .help = `#[{$name}]` can {$only}be applied to {$applied} + .suggestion = remove the attribute attr_parsing_empty_confusables = expected at least one confusable name @@ -168,3 +170,22 @@ attr_parsing_unused_multiple = -attr_parsing_previously_accepted = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +attr_parsing_meta_bad_delim = wrong meta list delimiters +attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` + +attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe + .label = usage of unsafe attribute +attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` + +attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute + .label = this is not an unsafe attribute + .suggestion = remove the `unsafe(...)` + .note = extraneous unsafe is not allowed in attributes + +attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr} + .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign + .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal + +attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes + .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 4d995027814b..088fa73d7427 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -1,14 +1,6 @@ use std::iter; -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::{MethodKind, Target}; -use rustc_span::{Span, Symbol, sym}; - -use super::{CombineAttributeParser, ConvertFn}; -use crate::context::MaybeWarn::{Allow, Warn}; -use crate::context::{AcceptContext, AllowedTargets, Stage}; -use crate::parser::ArgParser; +use super::prelude::*; use crate::session_diagnostics; pub(crate) struct AllowInternalUnstableParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/body.rs b/compiler/rustc_attr_parsing/src/attributes/body.rs index 88540384621d..a1492d761946 100644 --- a/compiler/rustc_attr_parsing/src/attributes/body.rs +++ b/compiler/rustc_attr_parsing/src/attributes/body.rs @@ -1,12 +1,6 @@ //! Attributes that can be found in function body. -use rustc_hir::Target; -use rustc_hir::attrs::AttributeKind; -use rustc_span::{Symbol, sym}; - -use super::{NoArgsAttributeParser, OnDuplicate}; -use crate::context::MaybeWarn::Allow; -use crate::context::{AllowedTargets, Stage}; +use super::prelude::*; pub(crate) struct CoroutineParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 6ea073896c2f..b884f8f38328 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,16 +1,7 @@ -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::attrs::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy}; -use rustc_hir::{MethodKind, Target}; +use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy}; use rustc_session::parse::feature_err; -use rustc_span::{Span, Symbol, sym}; -use super::{ - AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn, - NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, -}; -use crate::context::MaybeWarn::{Allow, Warn}; -use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage}; -use crate::parser::ArgParser; +use super::prelude::*; use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport}; pub(crate) struct OptimizeParser; @@ -44,7 +35,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option OptimizeAttr::Speed, Some(sym::none) => OptimizeAttr::DoNotOptimize, _ => { - cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]); + cx.expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]); OptimizeAttr::Default } }; @@ -91,7 +82,7 @@ impl SingleAttributeParser for CoverageParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let Some(args) = args.list() else { - cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]); + cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]); return None; }; @@ -100,7 +91,8 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option AttributeParser for UsedParser { UsedBy::Linker } _ => { - cx.expected_specific_argument(l.span(), vec!["compiler", "linker"]); + cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]); return; } } @@ -385,57 +377,68 @@ fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { } } +fn parse_tf_attribute<'c, S: Stage>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, +) -> impl IntoIterator + 'c { + let mut features = Vec::new(); + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span); + return features; + }; + if list.is_empty() { + cx.warn_empty_attribute(cx.attr_span); + return features; + } + for item in list.mixed() { + let Some(name_value) = item.meta_item() else { + cx.expected_name_value(item.span(), Some(sym::enable)); + return features; + }; + + // Validate name + let Some(name) = name_value.path().word_sym() else { + cx.expected_name_value(name_value.path().span(), Some(sym::enable)); + return features; + }; + if name != sym::enable { + cx.expected_name_value(name_value.path().span(), Some(sym::enable)); + return features; + } + + // Use value + let Some(name_value) = name_value.args().name_value() else { + cx.expected_name_value(item.span(), Some(sym::enable)); + return features; + }; + let Some(value_str) = name_value.value_as_str() else { + cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit())); + return features; + }; + for feature in value_str.as_str().split(",") { + features.push((Symbol::intern(feature), item.span())); + } + } + features +} + pub(crate) struct TargetFeatureParser; impl CombineAttributeParser for TargetFeatureParser { type Item = (Symbol, Span); const PATH: &[Symbol] = &[sym::target_feature]; - const CONVERT: ConvertFn = |items, span| AttributeKind::TargetFeature(items, span); + const CONVERT: ConvertFn = |items, span| AttributeKind::TargetFeature { + features: items, + attr_span: span, + was_forced: false, + }; const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]); fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, args: &'c ArgParser<'_>, ) -> impl IntoIterator + 'c { - let mut features = Vec::new(); - let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span); - return features; - }; - if list.is_empty() { - cx.warn_empty_attribute(cx.attr_span); - return features; - } - for item in list.mixed() { - let Some(name_value) = item.meta_item() else { - cx.expected_name_value(item.span(), Some(sym::enable)); - return features; - }; - - // Validate name - let Some(name) = name_value.path().word_sym() else { - cx.expected_name_value(name_value.path().span(), Some(sym::enable)); - return features; - }; - if name != sym::enable { - cx.expected_name_value(name_value.path().span(), Some(sym::enable)); - return features; - } - - // Use value - let Some(name_value) = name_value.args().name_value() else { - cx.expected_name_value(item.span(), Some(sym::enable)); - return features; - }; - let Some(value_str) = name_value.value_as_str() else { - cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit())); - return features; - }; - for feature in value_str.as_str().split(",") { - features.push((Symbol::intern(feature), item.span())); - } - } - features + parse_tf_attribute(cx, args) } const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ @@ -449,3 +452,131 @@ fn extend<'c>( Warn(Target::MacroDef), ]); } + +pub(crate) struct ForceTargetFeatureParser; + +impl CombineAttributeParser for ForceTargetFeatureParser { + type Item = (Symbol, Span); + const PATH: &[Symbol] = &[sym::force_target_feature]; + const CONVERT: ConvertFn = |items, span| AttributeKind::TargetFeature { + features: items, + attr_span: span, + was_forced: true, + }; + const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator + 'c { + parse_tf_attribute(cx, args) + } +} + +pub(crate) struct SanitizeParser; + +impl SingleAttributeParser for SanitizeParser { + const PATH: &[Symbol] = &[sym::sanitize]; + + // FIXME: still checked in check_attrs.rs + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + const TEMPLATE: AttributeTemplate = template!(List: &[ + r#"address = "on|off""#, + r#"kernel_address = "on|off""#, + r#"cfi = "on|off""#, + r#"hwaddress = "on|off""#, + r#"kcfi = "on|off""#, + r#"memory = "on|off""#, + r#"memtag = "on|off""#, + r#"shadow_call_stack = "on|off""#, + r#"thread = "on|off""# + ]); + + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + + let mut on_set = SanitizerSet::empty(); + let mut off_set = SanitizerSet::empty(); + + for item in list.mixed() { + let Some(item) = item.meta_item() else { + cx.expected_name_value(item.span(), None); + continue; + }; + + let path = item.path().word_sym(); + let Some(value) = item.args().name_value() else { + cx.expected_name_value(item.span(), path); + continue; + }; + + let mut apply = |s: SanitizerSet| { + let is_on = match value.value_as_str() { + Some(sym::on) => true, + Some(sym::off) => false, + Some(_) => { + cx.expected_specific_argument_strings( + value.value_span, + &[sym::on, sym::off], + ); + return; + } + None => { + cx.expected_string_literal(value.value_span, Some(value.value_as_lit())); + return; + } + }; + + if is_on { + on_set |= s; + } else { + off_set |= s; + } + }; + + match path { + Some(sym::address) | Some(sym::kernel_address) => { + apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS) + } + Some(sym::cfi) => apply(SanitizerSet::CFI), + Some(sym::kcfi) => apply(SanitizerSet::KCFI), + Some(sym::memory) => apply(SanitizerSet::MEMORY), + Some(sym::memtag) => apply(SanitizerSet::MEMTAG), + Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK), + Some(sym::thread) => apply(SanitizerSet::THREAD), + Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS), + _ => { + cx.expected_specific_argument_strings( + item.path().span(), + &[ + sym::address, + sym::cfi, + sym::kcfi, + sym::memory, + sym::memtag, + sym::shadow_call_stack, + sym::thread, + sym::hwaddress, + ], + ); + continue; + } + } + } + + Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 00f949c82c5a..97e78dfb136b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -1,13 +1,6 @@ -use rustc_feature::template; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::{MethodKind, Target}; -use rustc_span::{Span, Symbol, sym}; -use thin_vec::ThinVec; +use super::prelude::*; +use crate::session_diagnostics::EmptyConfusables; -use super::{AcceptMapping, AttributeParser}; -use crate::context::MaybeWarn::Allow; -use crate::context::{AllowedTargets, FinalizeContext, Stage}; -use crate::session_diagnostics; #[derive(Default)] pub(crate) struct ConfusablesParser { confusables: ThinVec, @@ -25,7 +18,7 @@ impl AttributeParser for ConfusablesParser { }; if list.is_empty() { - cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span }); + cx.emit_err(EmptyConfusables { span: cx.attr_span }); } for param in list.mixed() { diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs new file mode 100644 index 000000000000..9175d7479e12 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -0,0 +1,33 @@ +use super::prelude::*; + +pub(crate) struct CrateNameParser; + +impl SingleAttributeParser for CrateNameParser { + const PATH: &[Symbol] = &[sym::crate_name]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); + + // FIXME: crate name is allowed on all targets and ignored, + // even though it should only be valid on crates of course + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let ArgParser::NameValue(n) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + + let Some(name) = n.value_as_str() else { + cx.expected_string_literal(n.value_span, Some(n.value_as_lit())); + return None; + }; + + Some(AttributeKind::CrateName { + name, + name_span: n.value_span, + attr_span: cx.attr_span, + style: cx.attr_style, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 8101c91460f2..31c698228ef4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -1,14 +1,11 @@ -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation}; -use rustc_hir::{MethodKind, Target}; -use rustc_span::{Span, Symbol, sym}; +use rustc_hir::attrs::{DeprecatedSince, Deprecation}; +use super::prelude::*; use super::util::parse_version; -use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::MaybeWarn::{Allow, Error}; -use crate::context::{AcceptContext, AllowedTargets, Stage}; -use crate::parser::ArgParser; -use crate::session_diagnostics; +use crate::session_diagnostics::{ + DeprecatedItemSuggestion, InvalidSince, MissingNote, MissingSince, +}; + pub(crate) struct DeprecationParser; fn get( @@ -54,6 +51,8 @@ impl SingleAttributeParser for DeprecationParser { Allow(Target::TyAlias), Allow(Target::Use), Allow(Target::ForeignFn), + Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), Allow(Target::Field), Allow(Target::Trait), Allow(Target::AssocTy), @@ -100,7 +99,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { if !features.deprecated_suggestion() { - cx.emit_err(session_diagnostics::DeprecatedItemSuggestion { + cx.emit_err(DeprecatedItemSuggestion { span: param.span(), is_nightly: cx.sess().is_nightly_build(), details: (), @@ -142,18 +141,18 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option SingleAttributeParser for DummyParser { const PATH: &[Symbol] = &[sym::rustc_dummy]; diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index 6a659a95b856..39d5ff85d9f7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -2,17 +2,10 @@ // note: need to model better how duplicate attr errors work when not using // SingleAttributeParser which is what we have two of here. -use rustc_feature::{AttributeTemplate, template}; use rustc_hir::attrs::{AttributeKind, InlineAttr}; -use rustc_hir::lints::AttributeLintKind; -use rustc_hir::{MethodKind, Target}; -use rustc_span::{Symbol, sym}; -use super::{AcceptContext, AttributeOrder, OnDuplicate}; -use crate::attributes::SingleAttributeParser; -use crate::context::MaybeWarn::{Allow, Warn}; -use crate::context::{AllowedTargets, Stage}; -use crate::parser::ArgParser; +use super::prelude::*; + pub(crate) struct InlineParser; impl SingleAttributeParser for InlineParser { @@ -56,14 +49,14 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - cx.expected_specific_argument(l.span(), vec!["always", "never"]); + cx.expected_specific_argument(l.span(), &[sym::always, sym::never]); return None; } } } ArgParser::NameValue(_) => { - let suggestions = - >::TEMPLATE.suggestions(false, "inline"); + let suggestions = >::TEMPLATE + .suggestions(cx.attr_style, "inline"); let span = cx.attr_span; cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 552b9dfabc29..66b02697c771 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,16 +1,10 @@ -use rustc_feature::{AttributeTemplate, template}; use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection}; -use rustc_hir::attrs::{AttributeKind, Linkage}; -use rustc_hir::{MethodKind, Target}; -use rustc_span::{Span, Symbol, sym}; +use rustc_hir::attrs::Linkage; -use crate::attributes::{ - AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, -}; -use crate::context::MaybeWarn::Allow; -use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage, parse_single_integer}; -use crate::parser::ArgParser; +use super::prelude::*; +use super::util::parse_single_integer; use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection}; + pub(crate) struct LinkNameParser; impl SingleAttributeParser for LinkNameParser { @@ -212,16 +206,16 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { cx.expected_specific_argument( name_value.value_span, - vec![ - "available_externally", - "common", - "extern_weak", - "external", - "internal", - "linkonce", - "linkonce_odr", - "weak", - "weak_odr", + &[ + sym::available_externally, + sym::common, + sym::extern_weak, + sym::external, + sym::internal, + sym::linkonce, + sym::linkonce_odr, + sym::weak, + sym::weak_odr, ], ); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 2b586d4003cd..63b0809d0d8c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -1,10 +1,5 @@ -use rustc_hir::attrs::AttributeKind; -use rustc_hir::{MethodKind, Target}; -use rustc_span::{Span, Symbol, sym}; +use super::prelude::*; -use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; -use crate::context::MaybeWarn::{Allow, Error}; -use crate::context::{AllowedTargets, Stage}; pub(crate) struct AsPtrParser; impl NoArgsAttributeParser for AsPtrParser { const PATH: &[Symbol] = &[sym::rustc_as_ptr]; diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs index 242e2f2c1bc0..528090b8673d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs +++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs @@ -1,10 +1,5 @@ -use rustc_hir::Target; -use rustc_hir::attrs::AttributeKind; -use rustc_span::{Span, Symbol, sym}; +use super::prelude::*; -use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; -use crate::context::MaybeWarn::Allow; -use crate::context::{AllowedTargets, Stage}; pub(crate) struct LoopMatchParser; impl NoArgsAttributeParser for LoopMatchParser { const PATH: &[Symbol] = &[sym::loop_match]; diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index c9b5dd35fa1d..180130c7be4f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,15 +1,9 @@ use rustc_errors::DiagArgValue; -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::Target; -use rustc_hir::attrs::{AttributeKind, MacroUseArgs}; -use rustc_span::{Span, Symbol, sym}; -use thin_vec::ThinVec; +use rustc_hir::attrs::MacroUseArgs; + +use super::prelude::*; +use crate::session_diagnostics::IllFormedAttributeInputLint; -use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate}; -use crate::context::MaybeWarn::{Allow, Error, Warn}; -use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage}; -use crate::parser::ArgParser; -use crate::session_diagnostics; pub(crate) struct MacroEscapeParser; impl NoArgsAttributeParser for MacroEscapeParser { const PATH: &[Symbol] = &[sym::macro_escape]; @@ -107,8 +101,8 @@ impl AttributeParser for MacroUseParser { } } ArgParser::NameValue(_) => { - let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use); - cx.emit_err(session_diagnostics::IllFormedAttributeInputLint { + let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use); + cx.emit_err(IllFormedAttributeInputLint { num_suggestions: suggestions.len(), suggestions: DiagArgValue::StrListSepByAnd( suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index ed5d1d92b8ca..9dad9c893f0a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -7,9 +7,9 @@ //! Specifically, you might not care about managing the state of your [`AttributeParser`] //! state machine yourself. In this case you can choose to implement: //! -//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it +//! - [`SingleAttributeParser`](crate::attributes::SingleAttributeParser): makes it easy to implement an attribute which should error if it //! appears more than once in a list of attributes -//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the +//! - [`CombineAttributeParser`](crate::attributes::CombineAttributeParser): makes it easy to implement an attribute which should combine the //! contents of attributes, if an attribute appear multiple times in a list //! //! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed. @@ -21,9 +21,13 @@ use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; -use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage}; +use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics::UnusedMultiple; +use crate::target_checking::AllowedTargets; + +/// All the parsers require roughly the same imports, so this prelude has most of the often-needed ones. +mod prelude; pub(crate) mod allow_unstable; pub(crate) mod body; @@ -31,6 +35,7 @@ pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; pub(crate) mod confusables; +pub(crate) mod crate_level; pub(crate) mod deprecation; pub(crate) mod dummy; pub(crate) mod inline; @@ -43,6 +48,7 @@ pub(crate) mod non_exhaustive; pub(crate) mod path; pub(crate) mod proc_macro_attrs; +pub(crate) mod prototype; pub(crate) mod repr; pub(crate) mod rustc_internal; pub(crate) mod semantics; diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index b6cfc7805906..e6a5141d7830 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -1,19 +1,29 @@ use rustc_errors::DiagArgValue; -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::attrs::AttributeKind; -use rustc_span::{Symbol, sym}; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage}; -use crate::parser::ArgParser; -use crate::session_diagnostics; +use super::prelude::*; +use crate::session_diagnostics::IllFormedAttributeInputLint; + pub(crate) struct MustUseParser; impl SingleAttributeParser for MustUseParser { const PATH: &[Symbol] = &[sym::must_use]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + Allow(Target::Fn), + Allow(Target::Enum), + Allow(Target::Struct), + Allow(Target::Union), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::ForeignFn), + // `impl Trait` in return position can trip + // `unused_must_use` if `Trait` is marked as + // `#[must_use]` + Allow(Target::Trait), + Error(Target::WherePredicate), + ]); const TEMPLATE: AttributeTemplate = template!( Word, NameValueStr: "reason", "https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute" @@ -35,9 +45,9 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - let suggestions = - >::TEMPLATE.suggestions(false, "must_use"); - cx.emit_err(session_diagnostics::IllFormedAttributeInputLint { + let suggestions = >::TEMPLATE + .suggestions(cx.attr_style, "must_use"); + cx.emit_err(IllFormedAttributeInputLint { num_suggestions: suggestions.len(), suggestions: DiagArgValue::StrListSepByAnd( suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), diff --git a/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs b/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs index 589faf38f730..40073ea0f461 100644 --- a/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs @@ -1,10 +1,5 @@ -use rustc_hir::Target; -use rustc_hir::attrs::AttributeKind; -use rustc_span::{Span, sym}; +use super::prelude::*; -use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; -use crate::context::MaybeWarn::Allow; -use crate::context::{AllowedTargets, Stage}; pub(crate) struct NoImplicitPreludeParser; impl NoArgsAttributeParser for NoImplicitPreludeParser { diff --git a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs index 41e9ca4de410..4e6aec95e66e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs +++ b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs @@ -3,8 +3,10 @@ use rustc_span::{Span, Symbol, sym}; use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; -use crate::context::MaybeWarn::{Allow, Warn}; -use crate::context::{AllowedTargets, Stage}; +use crate::context::Stage; +use crate::target_checking::AllowedTargets; +use crate::target_checking::Policy::{Allow, Warn}; + pub(crate) struct NonExhaustiveParser; impl NoArgsAttributeParser for NonExhaustiveParser { diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs index f9191d1abed3..e4cb806bb427 100644 --- a/compiler/rustc_attr_parsing/src/attributes/path.rs +++ b/compiler/rustc_attr_parsing/src/attributes/path.rs @@ -1,12 +1,5 @@ -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::Target; -use rustc_hir::attrs::AttributeKind; -use rustc_span::{Symbol, sym}; +use super::prelude::*; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::MaybeWarn::{Allow, Error}; -use crate::context::{AcceptContext, AllowedTargets, Stage}; -use crate::parser::ArgParser; pub(crate) struct PathParser; impl SingleAttributeParser for PathParser { diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs new file mode 100644 index 000000000000..2bcdee55c756 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs @@ -0,0 +1,20 @@ +// parsing +// templates +pub(super) use rustc_feature::{AttributeTemplate, template}; +// data structures +pub(super) use rustc_hir::attrs::AttributeKind; +pub(super) use rustc_hir::lints::AttributeLintKind; +pub(super) use rustc_hir::{MethodKind, Target}; +pub(super) use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; +pub(super) use thin_vec::ThinVec; + +pub(super) use crate::attributes::{ + AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn, + NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, +}; +// contexts +pub(super) use crate::context::{AcceptContext, FinalizeContext, Stage}; +pub(super) use crate::parser::*; +// target checking +pub(super) use crate::target_checking::Policy::{Allow, Error, Warn}; +pub(super) use crate::target_checking::{ALL_TARGETS, AllowedTargets}; diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index 4624fa362875..9ac18c04e328 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -1,15 +1,5 @@ -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::Target; -use rustc_hir::attrs::AttributeKind; -use rustc_span::{Span, Symbol, sym}; -use thin_vec::ThinVec; +use super::prelude::*; -use crate::attributes::{ - AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, -}; -use crate::context::MaybeWarn::{Allow, Warn}; -use crate::context::{AcceptContext, AllowedTargets, Stage}; -use crate::parser::ArgParser; pub(crate) struct ProcMacroParser; impl NoArgsAttributeParser for ProcMacroParser { const PATH: &[Symbol] = &[sym::proc_macro]; @@ -110,7 +100,7 @@ fn parse_derive_like( return None; }; if !attr_list.path().word_is(sym::attributes) { - cx.expected_specific_argument(attrs.span(), vec!["attributes"]); + cx.expected_specific_argument(attrs.span(), &[sym::attributes]); return None; } let Some(attr_list) = attr_list.args().list() else { diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs new file mode 100644 index 000000000000..80fe82bf5429 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -0,0 +1,141 @@ +//! Attributes that are only used on function prototypes. + +use rustc_feature::{AttributeTemplate, template}; +use rustc_hir::Target; +use rustc_hir::attrs::{AttributeKind, MirDialect, MirPhase}; +use rustc_span::{Span, Symbol, sym}; + +use super::{AttributeOrder, OnDuplicate}; +use crate::attributes::SingleAttributeParser; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; +use crate::target_checking::AllowedTargets; +use crate::target_checking::Policy::Allow; + +pub(crate) struct CustomMirParser; + +impl SingleAttributeParser for CustomMirParser { + const PATH: &[rustc_span::Symbol] = &[sym::custom_mir]; + + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + + const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + + let mut dialect = None; + let mut phase = None; + let mut failed = false; + + for item in list.mixed() { + let Some(meta_item) = item.meta_item() else { + cx.expected_name_value(item.span(), None); + failed = true; + break; + }; + + if let Some(arg) = meta_item.word_is(sym::dialect) { + extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed); + } else if let Some(arg) = meta_item.word_is(sym::phase) { + extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed); + } else if let Some(word) = meta_item.path().word() { + let word = word.to_string(); + cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]); + failed = true; + } else { + cx.expected_name_value(meta_item.span(), None); + failed = true; + }; + } + + let dialect = parse_dialect(cx, dialect, &mut failed); + let phase = parse_phase(cx, phase, &mut failed); + + if failed { + return None; + } + + Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span)) + } +} + +fn extract_value( + cx: &mut AcceptContext<'_, '_, S>, + key: Symbol, + arg: &ArgParser<'_>, + span: Span, + out_val: &mut Option<(Symbol, Span)>, + failed: &mut bool, +) { + if out_val.is_some() { + cx.duplicate_key(span, key); + *failed = true; + return; + } + + let Some(val) = arg.name_value() else { + cx.expected_single_argument(arg.span().unwrap_or(span)); + *failed = true; + return; + }; + + let Some(value_sym) = val.value_as_str() else { + cx.expected_string_literal(val.value_span, Some(val.value_as_lit())); + *failed = true; + return; + }; + + *out_val = Some((value_sym, val.value_span)); +} + +fn parse_dialect( + cx: &mut AcceptContext<'_, '_, S>, + dialect: Option<(Symbol, Span)>, + failed: &mut bool, +) -> Option<(MirDialect, Span)> { + let (dialect, span) = dialect?; + + let dialect = match dialect { + sym::analysis => MirDialect::Analysis, + sym::built => MirDialect::Built, + sym::runtime => MirDialect::Runtime, + + _ => { + cx.expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]); + *failed = true; + return None; + } + }; + + Some((dialect, span)) +} + +fn parse_phase( + cx: &mut AcceptContext<'_, '_, S>, + phase: Option<(Symbol, Span)>, + failed: &mut bool, +) -> Option<(MirPhase, Span)> { + let (phase, span) = phase?; + + let phase = match phase { + sym::initial => MirPhase::Initial, + sym::post_cleanup => MirPhase::PostCleanup, + sym::optimized => MirPhase::Optimized, + + _ => { + cx.expected_specific_argument(span, &[sym::initial, sym::post_cleanup, sym::optimized]); + *failed = true; + return None; + } + }; + + Some((phase, span)) +} diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 7ab58ed93474..23aabd155976 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -1,16 +1,10 @@ use rustc_abi::Align; use rustc_ast::{IntTy, LitIntType, LitKind, UintTy}; -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::attrs::{AttributeKind, IntType, ReprAttr}; -use rustc_hir::{MethodKind, Target}; -use rustc_span::{DUMMY_SP, Span, Symbol, sym}; +use rustc_hir::attrs::{IntType, ReprAttr}; + +use super::prelude::*; +use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; -use super::{AcceptMapping, AttributeParser, CombineAttributeParser, ConvertFn, FinalizeContext}; -use crate::context::MaybeWarn::Allow; -use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage}; -use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser}; -use crate::session_diagnostics; -use crate::session_diagnostics::IncorrectReprFormatGenericCause; /// Parse #[repr(...)] forms. /// /// Valid repr contents: any of the primitive integral type names (see diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index efd7b650e441..a995549fc7c8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,12 +1,6 @@ -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::Target; -use rustc_hir::attrs::AttributeKind; -use rustc_span::{Symbol, sym}; +use super::prelude::*; +use super::util::parse_single_integer; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::MaybeWarn::Allow; -use crate::context::{AcceptContext, AllowedTargets, Stage, parse_single_integer}; -use crate::parser::ArgParser; pub(crate) struct RustcLayoutScalarValidRangeStart; impl SingleAttributeParser for RustcLayoutScalarValidRangeStart { diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs index d4ad861a3a22..d7f624832971 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -1,8 +1,5 @@ -use rustc_hir::attrs::AttributeKind; -use rustc_span::{Span, Symbol, sym}; +use super::prelude::*; -use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; -use crate::context::{ALL_TARGETS, AllowedTargets, Stage}; pub(crate) struct MayDangleParser; impl NoArgsAttributeParser for MayDangleParser { const PATH: &[Symbol] = &[sym::may_dangle]; diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 5a26178f84b5..b94e23477ffe 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -1,20 +1,13 @@ use std::num::NonZero; use rustc_errors::ErrorGuaranteed; -use rustc_feature::template; -use rustc_hir::attrs::AttributeKind; use rustc_hir::{ DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel, StableSince, Target, UnstableReason, VERSION_PLACEHOLDER, }; -use rustc_span::{Ident, Span, Symbol, sym}; +use super::prelude::*; use super::util::parse_version; -use super::{AcceptMapping, AttributeParser, OnDuplicate}; -use crate::attributes::NoArgsAttributeParser; -use crate::context::MaybeWarn::Allow; -use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage}; -use crate::parser::{ArgParser, MetaItemParser}; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; macro_rules! reject_outside_std { @@ -54,6 +47,7 @@ macro_rules! reject_outside_std { Allow(Target::Static), Allow(Target::ForeignFn), Allow(Target::ForeignStatic), + Allow(Target::ExternCrate), ]); #[derive(Default)] diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index 8b666c3868ba..510ff1ded490 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -1,13 +1,5 @@ -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::Target; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::lints::AttributeLintKind; -use rustc_span::{Symbol, sym}; +use super::prelude::*; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::MaybeWarn::{Allow, Error}; -use crate::context::{AcceptContext, AllowedTargets, Stage}; -use crate::parser::ArgParser; pub(crate) struct IgnoreParser; impl SingleAttributeParser for IgnoreParser { @@ -29,7 +21,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let Some(str_value) = name_value.value_as_str() else { let suggestions = >::TEMPLATE - .suggestions(false, "ignore"); + .suggestions(cx.attr_style, "ignore"); let span = cx.attr_span; cx.emit_lint( AttributeLintKind::IllFormedAttributeInput { suggestions }, @@ -40,8 +32,8 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - let suggestions = - >::TEMPLATE.suggestions(false, "ignore"); + let suggestions = >::TEMPLATE + .suggestions(cx.attr_style, "ignore"); let span = cx.attr_span; cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); return None; @@ -89,7 +81,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option SingleAttributeParser for SkipDuringMethodDispatchParser { const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch]; @@ -44,7 +42,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option (key, &mut array), Some(key @ sym::boxed_slice) => (key, &mut boxed_slice), _ => { - cx.expected_specific_argument(path.span(), vec!["array", "boxed_slice"]); + cx.expected_specific_argument(path.span(), &[sym::array, sym::boxed_slice]); continue; } }; diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index 0ffcf434b521..ea1f5549c4ec 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -1,13 +1,7 @@ -use rustc_feature::{AttributeTemplate, template}; -use rustc_hir::Target; -use rustc_hir::attrs::AttributeKind; use rustc_span::hygiene::Transparency; -use rustc_span::{Symbol, sym}; -use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::MaybeWarn::Allow; -use crate::context::{AcceptContext, AllowedTargets, Stage}; -use crate::parser::ArgParser; +use super::prelude::*; + pub(crate) struct TransparencyParser; // FIXME(jdonszelmann): make these proper diagnostics @@ -35,7 +29,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { cx.expected_specific_argument_strings( nv.value_span, - vec!["transparent", "semitransparent", "opaque"], + &[sym::transparent, sym::semitransparent, sym::opaque], ); None } diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 10134915b278..77e8c32e59d7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -1,8 +1,12 @@ -use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name}; +use rustc_ast::LitKind; +use rustc_ast::attr::AttributeExt; use rustc_feature::is_builtin_attr_name; use rustc_hir::RustcVersion; use rustc_span::{Symbol, sym}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; + /// Parse a rustc version number written inside string literal in an attribute, /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are /// not accepted in this position, unlike when parsing CFG_RELEASE. @@ -23,10 +27,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) } -pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option { - first_attr_value_str_by_name(attrs, sym::crate_name) -} - pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( attrs: impl Iterator, symbol: Symbol, @@ -56,3 +56,32 @@ pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( } false } + +/// Parse a single integer. +/// +/// Used by attributes that take a single integer as argument, such as +/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`. +/// `cx` is the context given to the attribute. +/// `args` is the parser for the attribute arguments. +pub(crate) fn parse_single_integer( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser<'_>, +) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + let Some(lit) = single.lit() else { + cx.expected_integer_literal(single.span()); + return None; + }; + let LitKind::Int(num, _ty) = lit.kind else { + cx.expected_integer_literal(single.span()); + return None; + }; + Some(num.0) +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index bebe3350c4e0..d4b9cfe00add 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -3,28 +3,28 @@ use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; -use itertools::Itertools; use private::Sealed; -use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId}; -use rustc_errors::{DiagCtxtHandle, Diagnostic}; -use rustc_feature::{AttributeTemplate, Features}; +use rustc_ast::{AttrStyle, MetaItemLit, NodeId}; +use rustc_errors::{Diag, Diagnostic, Level}; +use rustc_feature::AttributeTemplate; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; -use rustc_hir::{ - AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId, MethodKind, Target, -}; +use rustc_hir::{AttrPath, HirId}; use rustc_session::Session; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use crate::AttributeParser; use crate::attributes::allow_unstable::{ AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, }; use crate::attributes::body::CoroutineParser; use crate::attributes::codegen_attrs::{ - ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, - TargetFeatureParser, TrackCallerParser, UsedParser, + ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, + NoMangleParser, OptimizeParser, SanitizeParser, TargetFeatureParser, TrackCallerParser, + UsedParser, }; use crate::attributes::confusables::ConfusablesParser; +use crate::attributes::crate_level::CrateNameParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -46,6 +46,7 @@ use crate::attributes::proc_macro_attrs::{ ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, }; +use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, @@ -64,23 +65,21 @@ }; use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; -use crate::context::MaybeWarn::{Allow, Error, Warn}; -use crate::parser::{ArgParser, MetaItemParser, PathParser}; -use crate::session_diagnostics::{ - AttributeParseError, AttributeParseErrorReason, InvalidTarget, UnknownMetaItem, -}; +use crate::parser::{ArgParser, PathParser}; +use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem}; +use crate::target_checking::AllowedTargets; type GroupType = LazyLock>; -struct GroupTypeInner { - accepters: BTreeMap<&'static [Symbol], Vec>>, - finalizers: Vec>, +pub(super) struct GroupTypeInner { + pub(super) accepters: BTreeMap<&'static [Symbol], Vec>>, + pub(super) finalizers: Vec>, } -struct GroupTypeInnerAccept { - template: AttributeTemplate, - accept_fn: AcceptFn, - allowed_targets: AllowedTargets, +pub(super) struct GroupTypeInnerAccept { + pub(super) template: AttributeTemplate, + pub(super) accept_fn: AcceptFn, + pub(super) allowed_targets: AllowedTargets, } type AcceptFn = @@ -160,6 +159,7 @@ mod late { // tidy-alphabetical-start Combine, Combine, + Combine, Combine, Combine, Combine, @@ -167,6 +167,8 @@ mod late { // tidy-alphabetical-start Single, + Single, + Single, Single, Single, Single, @@ -185,6 +187,7 @@ mod late { Single, Single, Single, + Single, Single, Single, Single, @@ -262,11 +265,7 @@ fn emit_err<'sess>( sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>, ) -> ErrorGuaranteed { - if self.emit_errors.should_emit() { - sess.dcx().emit_err(diag) - } else { - sess.dcx().create_err(diag).delay_as_bug() - } + self.should_emit().emit_err(sess.dcx().create_err(diag)) } fn should_emit(&self) -> ShouldEmit { @@ -313,6 +312,9 @@ pub struct AcceptContext<'f, 'sess, S: Stage> { /// The span of the attribute currently being parsed pub(crate) attr_span: Span, + /// Whether it is an inner or outer attribute + pub(crate) attr_style: AttrStyle, + /// The expected structure of the attribute. /// /// Used in reporting errors to give a hint to users what the attribute *should* look like. @@ -331,7 +333,7 @@ pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuarant /// must be delayed until after HIR is built. This method will take care of the details of /// that. pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) { - if !self.stage.should_emit().should_emit() { + if matches!(self.stage.should_emit(), ShouldEmit::Nothing) { return; } let id = self.target_id; @@ -394,6 +396,7 @@ pub(crate) fn expected_string_literal( i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) }), }, + attr_style: self.attr_style, }) } @@ -404,6 +407,7 @@ pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedIntegerLiteral, + attr_style: self.attr_style, }) } @@ -414,6 +418,7 @@ pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedList, + attr_style: self.attr_style, }) } @@ -424,6 +429,7 @@ pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedNoArgs, + attr_style: self.attr_style, }) } @@ -435,6 +441,7 @@ pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedIdentifier, + attr_style: self.attr_style, }) } @@ -447,6 +454,7 @@ pub(crate) fn expected_name_value(&self, span: Span, name: Option) -> Er template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedNameValue(name), + attr_style: self.attr_style, }) } @@ -458,6 +466,7 @@ pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::DuplicateKey(key), + attr_style: self.attr_style, }) } @@ -470,6 +479,7 @@ pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::UnexpectedLiteral, + attr_style: self.attr_style, }) } @@ -480,6 +490,7 @@ pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedSingleArgument, + attr_style: self.attr_style, }) } @@ -490,13 +501,15 @@ pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuarante template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument, + attr_style: self.attr_style, }) } + /// produces an error along the lines of `expected one of [foo, meow]` pub(crate) fn expected_specific_argument( &self, span: Span, - possibilities: Vec<&'static str>, + possibilities: &[Symbol], ) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, @@ -508,13 +521,16 @@ pub(crate) fn expected_specific_argument( strings: false, list: false, }, + attr_style: self.attr_style, }) } + /// produces an error along the lines of `expected one of [foo, meow] as an argument`. + /// i.e. slightly different wording to [`expected_specific_argument`](Self::expected_specific_argument). pub(crate) fn expected_specific_argument_and_list( &self, span: Span, - possibilities: Vec<&'static str>, + possibilities: &[Symbol], ) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, @@ -526,13 +542,15 @@ pub(crate) fn expected_specific_argument_and_list( strings: false, list: true, }, + attr_style: self.attr_style, }) } + /// produces an error along the lines of `expected one of ["foo", "meow"]` pub(crate) fn expected_specific_argument_strings( &self, span: Span, - possibilities: Vec<&'static str>, + possibilities: &[Symbol], ) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, @@ -544,6 +562,7 @@ pub(crate) fn expected_specific_argument_strings( strings: true, list: false, }, + attr_style: self.attr_style, }) } @@ -579,7 +598,7 @@ pub struct SharedContext<'p, 'sess, S: Stage> { /// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to pub(crate) target_id: S::Id, - emit_lint: &'p mut dyn FnMut(AttributeLint), + pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint), } /// Context given to every attribute parser during finalization. @@ -632,8 +651,13 @@ pub enum OmitDoc { Skip, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum ShouldEmit { + /// The operations will emit errors, and lints, and errors are fatal. + /// + /// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`. + /// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible. + EarlyFatal, /// The operation will emit errors and lints. /// This is usually what you need. ErrorsAndLints, @@ -643,559 +667,12 @@ pub enum ShouldEmit { } impl ShouldEmit { - pub fn should_emit(&self) -> bool { + pub(crate) fn emit_err(&self, diag: Diag<'_>) -> ErrorGuaranteed { match self { - ShouldEmit::ErrorsAndLints => true, - ShouldEmit::Nothing => false, + ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(), + ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(), + ShouldEmit::ErrorsAndLints => diag.emit(), + ShouldEmit::Nothing => diag.delay_as_bug(), } } } - -#[derive(Debug)] -pub(crate) enum AllowedTargets { - AllowList(&'static [MaybeWarn]), - AllowListWarnRest(&'static [MaybeWarn]), -} - -pub(crate) enum AllowedResult { - Allowed, - Warn, - Error, -} - -impl AllowedTargets { - pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult { - match self { - AllowedTargets::AllowList(list) => { - if list.contains(&Allow(target)) { - AllowedResult::Allowed - } else if list.contains(&Warn(target)) { - AllowedResult::Warn - } else { - AllowedResult::Error - } - } - AllowedTargets::AllowListWarnRest(list) => { - if list.contains(&Allow(target)) { - AllowedResult::Allowed - } else if list.contains(&Error(target)) { - AllowedResult::Error - } else { - AllowedResult::Warn - } - } - } - } - - pub(crate) fn allowed_targets(&self) -> Vec { - match self { - AllowedTargets::AllowList(list) => list, - AllowedTargets::AllowListWarnRest(list) => list, - } - .iter() - .filter_map(|target| match target { - Allow(target) => Some(*target), - Warn(_) => None, - Error(_) => None, - }) - .collect() - } -} - -#[derive(Debug, Eq, PartialEq)] -pub(crate) enum MaybeWarn { - Allow(Target), - Warn(Target), - Error(Target), -} - -/// Context created once, for example as part of the ast lowering -/// context, through which all attributes can be lowered. -pub struct AttributeParser<'sess, S: Stage = Late> { - pub(crate) tools: Vec, - features: Option<&'sess Features>, - sess: &'sess Session, - stage: S, - - /// *Only* parse attributes with this symbol. - /// - /// Used in cases where we want the lowering infrastructure for parse just a single attribute. - parse_only: Option, -} - -impl<'sess> AttributeParser<'sess, Early> { - /// This method allows you to parse attributes *before* you have access to features or tools. - /// One example where this is necessary, is to parse `feature` attributes themselves for - /// example. - /// - /// Try to use this as little as possible. Attributes *should* be lowered during - /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would - /// crash if you tried to do so through [`parse_limited`](Self::parse_limited). - /// - /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with - /// that symbol are picked out of the list of instructions and parsed. Those are returned. - /// - /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while - /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed - /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors - pub fn parse_limited( - sess: &'sess Session, - attrs: &[ast::Attribute], - sym: Symbol, - target_span: Span, - target_node_id: NodeId, - features: Option<&'sess Features>, - ) -> Option { - let mut p = Self { - features, - tools: Vec::new(), - parse_only: Some(sym), - sess, - stage: Early { emit_errors: ShouldEmit::Nothing }, - }; - let mut parsed = p.parse_attribute_list( - attrs, - target_span, - target_node_id, - Target::Crate, // Does not matter, we're not going to emit errors anyways - OmitDoc::Skip, - std::convert::identity, - |_lint| { - panic!("can't emit lints here for now (nothing uses this atm)"); - }, - ); - assert!(parsed.len() <= 1); - - parsed.pop() - } - - pub fn parse_single( - sess: &'sess Session, - attr: &ast::Attribute, - target_span: Span, - target_node_id: NodeId, - features: Option<&'sess Features>, - emit_errors: ShouldEmit, - parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T, - template: &AttributeTemplate, - ) -> T { - let mut parser = Self { - features, - tools: Vec::new(), - parse_only: None, - sess, - stage: Early { emit_errors }, - }; - let ast::AttrKind::Normal(normal_attr) = &attr.kind else { - panic!("parse_single called on a doc attr") - }; - let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx()); - let path = meta_parser.path(); - let args = meta_parser.args(); - let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext { - shared: SharedContext { - cx: &mut parser, - target_span, - target_id: target_node_id, - emit_lint: &mut |_lint| { - panic!("can't emit lints here for now (nothing uses this atm)"); - }, - }, - attr_span: attr.span, - template, - attr_path: path.get_attribute_path(), - }; - parse_fn(&mut cx, args) - } -} - -impl<'sess, S: Stage> AttributeParser<'sess, S> { - pub fn new( - sess: &'sess Session, - features: &'sess Features, - tools: Vec, - stage: S, - ) -> Self { - Self { features: Some(features), tools, parse_only: None, sess, stage } - } - - pub(crate) fn sess(&self) -> &'sess Session { - &self.sess - } - - pub(crate) fn features(&self) -> &'sess Features { - self.features.expect("features not available at this point in the compiler") - } - - pub(crate) fn features_option(&self) -> Option<&'sess Features> { - self.features - } - - pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> { - self.sess().dcx() - } - - /// Parse a list of attributes. - /// - /// `target_span` is the span of the thing this list of attributes is applied to, - /// and when `omit_doc` is set, doc attributes are filtered out. - pub fn parse_attribute_list( - &mut self, - attrs: &[ast::Attribute], - target_span: Span, - target_id: S::Id, - target: Target, - omit_doc: OmitDoc, - - lower_span: impl Copy + Fn(Span) -> Span, - mut emit_lint: impl FnMut(AttributeLint), - ) -> Vec { - let mut attributes = Vec::new(); - let mut attr_paths = Vec::new(); - - for attr in attrs { - // If we're only looking for a single attribute, skip all the ones we don't care about. - if let Some(expected) = self.parse_only { - if !attr.has_name(expected) { - continue; - } - } - - // Sometimes, for example for `#![doc = include_str!("readme.md")]`, - // doc still contains a non-literal. You might say, when we're lowering attributes - // that's expanded right? But no, sometimes, when parsing attributes on macros, - // we already use the lowering logic and these are still there. So, when `omit_doc` - // is set we *also* want to ignore these. - if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) { - continue; - } - - match &attr.kind { - ast::AttrKind::DocComment(comment_kind, symbol) => { - if omit_doc == OmitDoc::Skip { - continue; - } - - attributes.push(Attribute::Parsed(AttributeKind::DocComment { - style: attr.style, - kind: *comment_kind, - span: lower_span(attr.span), - comment: *symbol, - })) - } - // // FIXME: make doc attributes go through a proper attribute parser - // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => { - // let p = GenericMetaItemParser::from_attr(&n, self.dcx()); - // - // attributes.push(Attribute::Parsed(AttributeKind::DocComment { - // style: attr.style, - // kind: CommentKind::Line, - // span: attr.span, - // comment: p.args().name_value(), - // })) - // } - ast::AttrKind::Normal(n) => { - attr_paths.push(PathParser::Ast(&n.item.path)); - - let parser = MetaItemParser::from_attr(n, self.dcx()); - let path = parser.path(); - let args = parser.args(); - let parts = path.segments().map(|i| i.name).collect::>(); - - if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) { - for accept in accepts { - let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { - shared: SharedContext { - cx: self, - target_span, - target_id, - emit_lint: &mut emit_lint, - }, - attr_span: lower_span(attr.span), - template: &accept.template, - attr_path: path.get_attribute_path(), - }; - - (accept.accept_fn)(&mut cx, args); - - if self.stage.should_emit().should_emit() { - match accept.allowed_targets.is_allowed(target) { - AllowedResult::Allowed => {} - AllowedResult::Warn => { - let allowed_targets = - accept.allowed_targets.allowed_targets(); - let (applied, only) = allowed_targets_applied( - allowed_targets, - target, - self.features, - ); - emit_lint(AttributeLint { - id: target_id, - span: attr.span, - kind: AttributeLintKind::InvalidTarget { - name: parts[0], - target, - only: if only { "only " } else { "" }, - applied, - }, - }); - } - AllowedResult::Error => { - let allowed_targets = - accept.allowed_targets.allowed_targets(); - let (applied, only) = allowed_targets_applied( - allowed_targets, - target, - self.features, - ); - self.dcx().emit_err(InvalidTarget { - span: attr.span, - name: parts[0], - target: target.plural_name(), - only: if only { "only " } else { "" }, - applied, - }); - } - } - } - } - } else { - // If we're here, we must be compiling a tool attribute... Or someone - // forgot to parse their fancy new attribute. Let's warn them in any case. - // If you are that person, and you really think your attribute should - // remain unparsed, carefully read the documentation in this module and if - // you still think so you can add an exception to this assertion. - - // FIXME(jdonszelmann): convert other attributes, and check with this that - // we caught em all - // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg]; - // assert!( - // self.tools.contains(&parts[0]) || true, - // // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]), - // "attribute {path} wasn't parsed and isn't a know tool attribute", - // ); - - attributes.push(Attribute::Unparsed(Box::new(AttrItem { - path: AttrPath::from_ast(&n.item.path), - args: self.lower_attr_args(&n.item.args, lower_span), - id: HashIgnoredAttrId { attr_id: attr.id }, - style: attr.style, - span: lower_span(attr.span), - }))); - } - } - } - } - - let mut parsed_attributes = Vec::new(); - for f in &S::parsers().finalizers { - if let Some(attr) = f(&mut FinalizeContext { - shared: SharedContext { - cx: self, - target_span, - target_id, - emit_lint: &mut emit_lint, - }, - all_attrs: &attr_paths, - }) { - parsed_attributes.push(Attribute::Parsed(attr)); - } - } - - attributes.extend(parsed_attributes); - - attributes - } - - /// Returns whether there is a parser for an attribute with this name - pub fn is_parsed_attribute(path: &[Symbol]) -> bool { - Late::parsers().accepters.contains_key(path) - } - - fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { - match args { - ast::AttrArgs::Empty => AttrArgs::Empty, - ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()), - // This is an inert key-value attribute - it will never be visible to macros - // after it gets lowered to HIR. Therefore, we can extract literals to handle - // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). - ast::AttrArgs::Eq { eq_span, expr } => { - // In valid code the value always ends up as a single literal. Otherwise, a dummy - // literal suffices because the error is handled elsewhere. - let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind - && let Ok(lit) = - ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span)) - { - lit - } else { - let guar = self.dcx().span_delayed_bug( - args.span().unwrap_or(DUMMY_SP), - "expr in place where literal is expected (builtin attr parsing)", - ); - ast::MetaItemLit { - symbol: sym::dummy, - suffix: None, - kind: ast::LitKind::Err(guar), - span: DUMMY_SP, - } - }; - AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit } - } - } - } -} - -/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to. -/// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string -pub(crate) fn allowed_targets_applied( - mut allowed_targets: Vec, - target: Target, - features: Option<&Features>, -) -> (String, bool) { - // Remove unstable targets from `allowed_targets` if their features are not enabled - if let Some(features) = features { - if !features.fn_delegation() { - allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. })); - } - if !features.stmt_expr_attributes() { - allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement)); - } - } - - // We define groups of "similar" targets. - // If at least two of the targets are allowed, and the `target` is not in the group, - // we collapse the entire group to a single entry to simplify the target list - const FUNCTION_LIKE: &[Target] = &[ - Target::Fn, - Target::Closure, - Target::ForeignFn, - Target::Method(MethodKind::Inherent), - Target::Method(MethodKind::Trait { body: false }), - Target::Method(MethodKind::Trait { body: true }), - Target::Method(MethodKind::TraitImpl), - ]; - const METHOD_LIKE: &[Target] = &[ - Target::Method(MethodKind::Inherent), - Target::Method(MethodKind::Trait { body: false }), - Target::Method(MethodKind::Trait { body: true }), - Target::Method(MethodKind::TraitImpl), - ]; - const IMPL_LIKE: &[Target] = - &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }]; - const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum]; - - let mut added_fake_targets = Vec::new(); - filter_targets( - &mut allowed_targets, - FUNCTION_LIKE, - "functions", - target, - &mut added_fake_targets, - ); - filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets); - filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets); - filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets); - - // If there is now only 1 target left, show that as the only possible target - ( - added_fake_targets - .iter() - .copied() - .chain(allowed_targets.iter().map(|t| t.plural_name())) - .join(", "), - allowed_targets.len() + added_fake_targets.len() == 1, - ) -} - -fn filter_targets( - allowed_targets: &mut Vec, - target_group: &'static [Target], - target_group_name: &'static str, - target: Target, - added_fake_targets: &mut Vec<&'static str>, -) { - if target_group.contains(&target) { - return; - } - if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 { - return; - } - allowed_targets.retain(|t| !target_group.contains(t)); - added_fake_targets.push(target_group_name); -} - -/// This is the list of all targets to which a attribute can be applied -/// This is used for: -/// - `rustc_dummy`, which can be applied to all targets -/// - Attributes that are not parted to the new target system yet can use this list as a placeholder -pub(crate) const ALL_TARGETS: &'static [MaybeWarn] = &[ - Allow(Target::ExternCrate), - Allow(Target::Use), - Allow(Target::Static), - Allow(Target::Const), - Allow(Target::Fn), - Allow(Target::Closure), - Allow(Target::Mod), - Allow(Target::ForeignMod), - Allow(Target::GlobalAsm), - Allow(Target::TyAlias), - Allow(Target::Enum), - Allow(Target::Variant), - Allow(Target::Struct), - Allow(Target::Field), - Allow(Target::Union), - Allow(Target::Trait), - Allow(Target::TraitAlias), - Allow(Target::Impl { of_trait: false }), - Allow(Target::Impl { of_trait: true }), - Allow(Target::Expression), - Allow(Target::Statement), - Allow(Target::Arm), - Allow(Target::AssocConst), - Allow(Target::Method(MethodKind::Inherent)), - Allow(Target::Method(MethodKind::Trait { body: false })), - Allow(Target::Method(MethodKind::Trait { body: true })), - Allow(Target::Method(MethodKind::TraitImpl)), - Allow(Target::AssocTy), - Allow(Target::ForeignFn), - Allow(Target::ForeignStatic), - Allow(Target::ForeignTy), - Allow(Target::MacroDef), - Allow(Target::Param), - Allow(Target::PatField), - Allow(Target::ExprField), - Allow(Target::WherePredicate), - Allow(Target::MacroCall), - Allow(Target::Crate), - Allow(Target::Delegation { mac: false }), - Allow(Target::Delegation { mac: true }), -]; - -/// Parse a single integer. -/// -/// Used by attributes that take a single integer as argument, such as -/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`. -/// `cx` is the context given to the attribute. -/// `args` is the parser for the attribute arguments. -pub(crate) fn parse_single_integer( - cx: &mut AcceptContext<'_, '_, S>, - args: &ArgParser<'_>, -) -> Option { - let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); - return None; - }; - let Some(single) = list.single() else { - cx.expected_single_argument(list.span); - return None; - }; - let Some(lit) = single.lit() else { - cx.expected_integer_literal(single.span()); - return None; - }; - let LitKind::Int(num, _ty) = lit.kind else { - cx.expected_integer_literal(single.span()); - return None; - }; - Some(num.0) -} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs new file mode 100644 index 000000000000..60523c2877c0 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -0,0 +1,370 @@ +use std::borrow::Cow; + +use rustc_ast as ast; +use rustc_ast::NodeId; +use rustc_errors::DiagCtxtHandle; +use rustc_feature::{AttributeTemplate, Features}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::lints::AttributeLint; +use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target}; +use rustc_session::Session; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; + +use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage}; +use crate::parser::{ArgParser, MetaItemParser, PathParser}; +use crate::{Early, Late, OmitDoc, ShouldEmit}; + +/// Context created once, for example as part of the ast lowering +/// context, through which all attributes can be lowered. +pub struct AttributeParser<'sess, S: Stage = Late> { + pub(crate) tools: Vec, + pub(crate) features: Option<&'sess Features>, + pub(crate) sess: &'sess Session, + pub(crate) stage: S, + + /// *Only* parse attributes with this symbol. + /// + /// Used in cases where we want the lowering infrastructure for parse just a single attribute. + parse_only: Option, +} + +impl<'sess> AttributeParser<'sess, Early> { + /// This method allows you to parse attributes *before* you have access to features or tools. + /// One example where this is necessary, is to parse `feature` attributes themselves for + /// example. + /// + /// Try to use this as little as possible. Attributes *should* be lowered during + /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would + /// crash if you tried to do so through [`parse_limited`](Self::parse_limited). + /// + /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with + /// that symbol are picked out of the list of instructions and parsed. Those are returned. + /// + /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while + /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed + /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors + pub fn parse_limited( + sess: &'sess Session, + attrs: &[ast::Attribute], + sym: Symbol, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + ) -> Option { + Self::parse_limited_should_emit( + sess, + attrs, + sym, + target_span, + target_node_id, + features, + ShouldEmit::Nothing, + ) + } + + /// Usually you want `parse_limited`, which defaults to no errors. + pub fn parse_limited_should_emit( + sess: &'sess Session, + attrs: &[ast::Attribute], + sym: Symbol, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + should_emit: ShouldEmit, + ) -> Option { + let mut parsed = Self::parse_limited_all( + sess, + attrs, + Some(sym), + Target::Crate, // Does not matter, we're not going to emit errors anyways + target_span, + target_node_id, + features, + should_emit, + ); + assert!(parsed.len() <= 1); + parsed.pop() + } + + pub fn parse_limited_all( + sess: &'sess Session, + attrs: &[ast::Attribute], + parse_only: Option, + target: Target, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + emit_errors: ShouldEmit, + ) -> Vec { + let mut p = + Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } }; + p.parse_attribute_list( + attrs, + target_span, + target_node_id, + target, + OmitDoc::Skip, + std::convert::identity, + |lint| { + crate::lints::emit_attribute_lint(&lint, sess); + }, + ) + } + + pub fn parse_single( + sess: &'sess Session, + attr: &ast::Attribute, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + emit_errors: ShouldEmit, + parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option, + template: &AttributeTemplate, + ) -> Option { + let mut parser = Self { + features, + tools: Vec::new(), + parse_only: None, + sess, + stage: Early { emit_errors }, + }; + let ast::AttrKind::Normal(normal_attr) = &attr.kind else { + panic!("parse_single called on a doc attr") + }; + let parts = + normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); + let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?; + let path = meta_parser.path(); + let args = meta_parser.args(); + let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext { + shared: SharedContext { + cx: &mut parser, + target_span, + target_id: target_node_id, + emit_lint: &mut |lint| { + crate::lints::emit_attribute_lint(&lint, sess); + }, + }, + attr_span: attr.span, + attr_style: attr.style, + template, + attr_path: path.get_attribute_path(), + }; + parse_fn(&mut cx, args) + } +} + +impl<'sess, S: Stage> AttributeParser<'sess, S> { + pub fn new( + sess: &'sess Session, + features: &'sess Features, + tools: Vec, + stage: S, + ) -> Self { + Self { features: Some(features), tools, parse_only: None, sess, stage } + } + + pub(crate) fn sess(&self) -> &'sess Session { + &self.sess + } + + pub(crate) fn features(&self) -> &'sess Features { + self.features.expect("features not available at this point in the compiler") + } + + pub(crate) fn features_option(&self) -> Option<&'sess Features> { + self.features + } + + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> { + self.sess().dcx() + } + + /// Parse a list of attributes. + /// + /// `target_span` is the span of the thing this list of attributes is applied to, + /// and when `omit_doc` is set, doc attributes are filtered out. + pub fn parse_attribute_list( + &mut self, + attrs: &[ast::Attribute], + target_span: Span, + target_id: S::Id, + target: Target, + omit_doc: OmitDoc, + + lower_span: impl Copy + Fn(Span) -> Span, + mut emit_lint: impl FnMut(AttributeLint), + ) -> Vec { + let mut attributes = Vec::new(); + let mut attr_paths = Vec::new(); + + for attr in attrs { + // If we're only looking for a single attribute, skip all the ones we don't care about. + if let Some(expected) = self.parse_only { + if !attr.has_name(expected) { + continue; + } + } + + // Sometimes, for example for `#![doc = include_str!("readme.md")]`, + // doc still contains a non-literal. You might say, when we're lowering attributes + // that's expanded right? But no, sometimes, when parsing attributes on macros, + // we already use the lowering logic and these are still there. So, when `omit_doc` + // is set we *also* want to ignore these. + if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) { + continue; + } + + match &attr.kind { + ast::AttrKind::DocComment(comment_kind, symbol) => { + if omit_doc == OmitDoc::Skip { + continue; + } + + attributes.push(Attribute::Parsed(AttributeKind::DocComment { + style: attr.style, + kind: *comment_kind, + span: lower_span(attr.span), + comment: *symbol, + })) + } + // // FIXME: make doc attributes go through a proper attribute parser + // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => { + // let p = GenericMetaItemParser::from_attr(&n, self.dcx()); + // + // attributes.push(Attribute::Parsed(AttributeKind::DocComment { + // style: attr.style, + // kind: CommentKind::Line, + // span: attr.span, + // comment: p.args().name_value(), + // })) + // } + ast::AttrKind::Normal(n) => { + attr_paths.push(PathParser(Cow::Borrowed(&n.item.path))); + + let parts = + n.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); + + if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) { + let Some(parser) = MetaItemParser::from_attr( + n, + &parts, + &self.sess.psess, + self.stage.should_emit(), + ) else { + continue; + }; + let path = parser.path(); + let args = parser.args(); + for accept in accepts { + let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { + shared: SharedContext { + cx: self, + target_span, + target_id, + emit_lint: &mut emit_lint, + }, + attr_span: lower_span(attr.span), + attr_style: attr.style, + template: &accept.template, + attr_path: path.get_attribute_path(), + }; + + (accept.accept_fn)(&mut cx, args); + + if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) { + self.check_target( + path.get_attribute_path(), + attr.span, + &accept.allowed_targets, + target, + target_id, + &mut emit_lint, + ); + } + } + } else { + // If we're here, we must be compiling a tool attribute... Or someone + // forgot to parse their fancy new attribute. Let's warn them in any case. + // If you are that person, and you really think your attribute should + // remain unparsed, carefully read the documentation in this module and if + // you still think so you can add an exception to this assertion. + + // FIXME(jdonszelmann): convert other attributes, and check with this that + // we caught em all + // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg]; + // assert!( + // self.tools.contains(&parts[0]) || true, + // // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]), + // "attribute {path} wasn't parsed and isn't a know tool attribute", + // ); + + attributes.push(Attribute::Unparsed(Box::new(AttrItem { + path: AttrPath::from_ast(&n.item.path), + args: self.lower_attr_args(&n.item.args, lower_span), + id: HashIgnoredAttrId { attr_id: attr.id }, + style: attr.style, + span: lower_span(attr.span), + }))); + } + } + } + } + + let mut parsed_attributes = Vec::new(); + for f in &S::parsers().finalizers { + if let Some(attr) = f(&mut FinalizeContext { + shared: SharedContext { + cx: self, + target_span, + target_id, + emit_lint: &mut emit_lint, + }, + all_attrs: &attr_paths, + }) { + parsed_attributes.push(Attribute::Parsed(attr)); + } + } + + attributes.extend(parsed_attributes); + + attributes + } + + /// Returns whether there is a parser for an attribute with this name + pub fn is_parsed_attribute(path: &[Symbol]) -> bool { + Late::parsers().accepters.contains_key(path) + } + + fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { + match args { + ast::AttrArgs::Empty => AttrArgs::Empty, + ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()), + // This is an inert key-value attribute - it will never be visible to macros + // after it gets lowered to HIR. Therefore, we can extract literals to handle + // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). + ast::AttrArgs::Eq { eq_span, expr } => { + // In valid code the value always ends up as a single literal. Otherwise, a dummy + // literal suffices because the error is handled elsewhere. + let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind + && let Ok(lit) = + ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span)) + { + lit + } else { + let guar = self.dcx().span_delayed_bug( + args.span().unwrap_or(DUMMY_SP), + "expr in place where literal is expected (builtin attr parsing)", + ); + ast::MetaItemLit { + symbol: sym::dummy, + suffix: None, + kind: ast::LitKind::Err(guar), + span: DUMMY_SP, + } + }; + AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit } + } + } + } +} diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index fc1377e53143..4dd908cdc40b 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -84,18 +84,31 @@ // tidy-alphabetical-end #[macro_use] +/// All the individual attribute parsers for each of rustc's built-in attributes. mod attributes; + +/// All the important types given to attribute parsers when parsing pub(crate) mod context; -mod lints; + +/// Code that other crates interact with, to actually parse a list (or sometimes single) +/// attribute. +mod interface; + +/// Despite this entire module called attribute parsing and the term being a little overloaded, +/// in this module the code lives that actually breaks up tokenstreams into semantic pieces of attributes, +/// like lists or name-value pairs. pub mod parser; + +mod lints; mod session_diagnostics; +mod target_checking; +pub mod validate_attr; pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr}; pub use attributes::cfg_old::*; -pub use attributes::util::{ - find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, -}; -pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit}; +pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version}; +pub use context::{Early, Late, OmitDoc, ShouldEmit}; +pub use interface::AttributeParser; pub use lints::emit_attribute_lint; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index 733225bab598..069478e7f0c8 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -1,11 +1,13 @@ +use std::borrow::Cow; + use rustc_errors::{DiagArgValue, LintEmitter}; +use rustc_hir::Target; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; -use rustc_hir::{HirId, Target}; use rustc_span::sym; use crate::session_diagnostics; -pub fn emit_attribute_lint(lint: &AttributeLint, lint_emitter: L) { +pub fn emit_attribute_lint(lint: &AttributeLint, lint_emitter: L) { let AttributeLint { id, span, kind } = lint; match kind { @@ -35,12 +37,12 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi *first_span, session_diagnostics::EmptyAttributeList { attr_span: *first_span }, ), - &AttributeLintKind::InvalidTarget { name, target, ref applied, only } => lint_emitter + AttributeLintKind::InvalidTarget { name, target, applied, only } => lint_emitter .emit_node_span_lint( // This check is here because `deprecated` had its own lint group and removing this would be a breaking change - if name == sym::deprecated + if name.segments[0].name == sym::deprecated && ![Target::Closure, Target::Expression, Target::Statement, Target::Arm] - .contains(&target) + .contains(target) { rustc_session::lint::builtin::USELESS_DEPRECATED } else { @@ -49,10 +51,13 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi *id, *span, session_diagnostics::InvalidTargetLint { - name, + name: name.clone(), target: target.plural_name(), - applied: applied.clone(), + applied: DiagArgValue::StrListSepByAnd( + applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(), + ), only, + attr_span: *span, }, ), } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index aecaae947c99..6d3cf684296b 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -3,45 +3,30 @@ //! //! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs` +use std::borrow::Cow; use std::fmt::{Debug, Display}; -use std::iter::Peekable; -use rustc_ast::token::{self, Delimiter, Token}; -use rustc_ast::tokenstream::{TokenStreamIter, TokenTree}; +use rustc_ast::token::{self, Delimiter, MetaVarKind}; +use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path}; use rustc_ast_pretty::pprust; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::PResult; use rustc_hir::{self as hir, AttrPath}; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; +use rustc_parse::exp; +use rustc_parse::parser::{Parser, PathStyle, token_descr}; +use rustc_session::errors::report_lit_error; +use rustc_session::parse::ParseSess; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym}; +use thin_vec::ThinVec; -pub struct SegmentIterator<'a> { - offset: usize, - path: &'a PathParser<'a>, -} - -impl<'a> Iterator for SegmentIterator<'a> { - type Item = &'a Ident; - - fn next(&mut self) -> Option { - if self.offset >= self.path.len() { - return None; - } - - let res = match self.path { - PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident, - PathParser::Attr(attr_path) => &attr_path.segments[self.offset], - }; - - self.offset += 1; - Some(res) - } -} +use crate::ShouldEmit; +use crate::session_diagnostics::{ + InvalidMetaItem, InvalidMetaItemQuoteIdentSugg, InvalidMetaItemRemoveNegSugg, MetaBadDelim, + MetaBadDelimSugg, SuffixedLiteralInAttribute, +}; #[derive(Clone, Debug)] -pub enum PathParser<'a> { - Ast(&'a Path), - Attr(AttrPath), -} +pub struct PathParser<'a>(pub Cow<'a, Path>); impl<'a> PathParser<'a> { pub fn get_attribute_path(&self) -> hir::AttrPath { @@ -52,21 +37,15 @@ pub fn get_attribute_path(&self) -> hir::AttrPath { } pub fn segments(&'a self) -> impl Iterator { - SegmentIterator { offset: 0, path: self } + self.0.segments.iter().map(|seg| &seg.ident) } pub fn span(&self) -> Span { - match self { - PathParser::Ast(path) => path.span, - PathParser::Attr(attr_path) => attr_path.span, - } + self.0.span } pub fn len(&self) -> usize { - match self { - PathParser::Ast(path) => path.segments.len(), - PathParser::Attr(attr_path) => attr_path.segments.len(), - } + self.0.segments.len() } pub fn segments_is(&self, segments: &[Symbol]) -> bool { @@ -99,10 +78,7 @@ pub fn starts_with(&self, segments: &[Symbol]) -> bool { impl Display for PathParser<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)), - PathParser::Attr(attr_path) => write!(f, "{attr_path}"), - } + write!(f, "{}", pprust::path_to_string(&self.0)) } } @@ -123,21 +99,39 @@ pub fn span(&self) -> Option { } } - pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self { - match value { + pub fn from_attr_args<'sess>( + value: &'a AttrArgs, + parts: &[Symbol], + psess: &'sess ParseSess, + should_emit: ShouldEmit, + ) -> Option { + Some(match value { AttrArgs::Empty => Self::NoArgs, - AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => { - Self::List(MetaItemListParser::new(args, dcx)) - } AttrArgs::Delimited(args) => { - Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() }) + // The arguments of rustc_dummy are not validated if the arguments are delimited + if parts == &[sym::rustc_dummy] { + return Some(ArgParser::List(MetaItemListParser { + sub_parsers: ThinVec::new(), + span: args.dspan.entire(), + })); + } + + if args.delim != Delimiter::Parenthesis { + psess.dcx().emit_err(MetaBadDelim { + span: args.dspan.entire(), + sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close }, + }); + return None; + } + + Self::List(MetaItemListParser::new(args, psess, should_emit)?) } AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser { eq_span: *eq_span, - value: expr_to_lit(dcx, &expr, *eq_span), + value: expr_to_lit(psess, &expr, expr.span, should_emit)?, value_span: expr.span, }), - } + }) } /// Asserts that this MetaItem is a list @@ -249,11 +243,16 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { impl<'a> MetaItemParser<'a> { /// Create a new parser from a [`NormalAttr`], which is stored inside of any /// [`ast::Attribute`](rustc_ast::Attribute) - pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self { - Self { - path: PathParser::Ast(&attr.item.path), - args: ArgParser::from_attr_args(&attr.item.args, dcx), - } + pub fn from_attr<'sess>( + attr: &'a NormalAttr, + parts: &[Symbol], + psess: &'sess ParseSess, + should_emit: ShouldEmit, + ) -> Option { + Some(Self { + path: PathParser(Cow::Borrowed(&attr.item.path)), + args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?, + }) } } @@ -318,215 +317,235 @@ pub fn value_as_str(&self) -> Option { } } -fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit { - // In valid code the value always ends up as a single literal. Otherwise, a dummy - // literal suffices because the error is handled elsewhere. - if let ExprKind::Lit(token_lit) = expr.kind - && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span) - { - lit +fn expr_to_lit( + psess: &ParseSess, + expr: &Expr, + span: Span, + should_emit: ShouldEmit, +) -> Option { + if let ExprKind::Lit(token_lit) = expr.kind { + let res = MetaItemLit::from_token_lit(token_lit, expr.span); + match res { + Ok(lit) => { + if token_lit.suffix.is_some() { + should_emit.emit_err( + psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), + ); + None + } else { + if !lit.kind.is_unsuffixed() { + // Emit error and continue, we can still parse the attribute as if the suffix isn't there + should_emit.emit_err( + psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), + ); + } + + Some(lit) + } + } + Err(err) => { + let guar = report_lit_error(psess, err, token_lit, expr.span); + let lit = MetaItemLit { + symbol: token_lit.symbol, + suffix: token_lit.suffix, + kind: LitKind::Err(guar), + span: expr.span, + }; + Some(lit) + } + } } else { - let guar = dcx.span_delayed_bug( - span, - "expr in place where literal is expected (builtin attr parsing)", - ); - MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span } + if matches!(should_emit, ShouldEmit::Nothing) { + return None; + } + + // Example cases: + // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`. + // - `#[foo = include_str!("nonexistent-file.rs")]`: + // results in `ast::ExprKind::Err`. In that case we delay + // the error because an earlier error will have already + // been reported. + let msg = "attribute value must be a literal"; + let err = psess.dcx().struct_span_err(span, msg); + should_emit.emit_err(err); + None } } struct MetaItemListParserContext<'a, 'sess> { - // the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside - inside_delimiters: Peekable>, - dcx: DiagCtxtHandle<'sess>, + parser: &'a mut Parser<'sess>, + should_emit: ShouldEmit, } impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { - fn done(&mut self) -> bool { - self.inside_delimiters.peek().is_none() - } + fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> { + let uninterpolated_span = self.parser.token_uninterpolated_span(); + let Some(token_lit) = self.parser.eat_token_lit() else { + return self.parser.handle_missing_lit(Parser::mk_meta_item_lit_char); + }; - fn next_path(&mut self) -> Option { - // FIXME: Share code with `parse_path`. - let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt)); - - match tt.as_deref()? { - &TokenTree::Token( - Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span }, - _, - ) => { - // here we have either an ident or pathsep `::`. - - let mut segments = if let &token::Ident(name, _) = kind { - // when we lookahead another pathsep, more path's coming - if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = - self.inside_delimiters.peek() - { - self.inside_delimiters.next(); - vec![Ident::new(name, span)] - } else { - // else we have a single identifier path, that's all - return Some(AttrPath { - segments: vec![Ident::new(name, span)].into_boxed_slice(), - span, - }); - } - } else { - // if `::` is all we get, we just got a path root - vec![Ident::new(kw::PathRoot, span)] - }; - - // one segment accepted. accept n more - loop { - // another ident? - if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) = - self.inside_delimiters - .next() - .map(|tt| TokenTree::uninterpolate(tt)) - .as_deref() - { - segments.push(Ident::new(name, span)); - } else { - return None; - } - // stop unless we see another `::` - if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = - self.inside_delimiters.peek() - { - self.inside_delimiters.next(); - } else { - break; - } - } - let span = span.with_hi(segments.last().unwrap().span.hi()); - Some(AttrPath { segments: segments.into_boxed_slice(), span }) + let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) { + Ok(lit) => lit, + Err(err) => { + let guar = + report_lit_error(&self.parser.psess, err, token_lit, uninterpolated_span); + // Pack possible quotes and prefixes from the original literal into + // the error literal's symbol so they can be pretty-printed faithfully. + let suffixless_lit = token::Lit::new(token_lit.kind, token_lit.symbol, None); + let symbol = Symbol::intern(&suffixless_lit.to_string()); + let token_lit = token::Lit::new(token::Err(guar), symbol, token_lit.suffix); + MetaItemLit::from_token_lit(token_lit, uninterpolated_span).unwrap() } - TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None, - _ => { - // malformed attributes can get here. We can't crash, but somewhere else should've - // already warned for this. - None - } - } - } + }; - fn value(&mut self) -> Option { - match self.inside_delimiters.next() { - Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => { - MetaItemListParserContext { - inside_delimiters: inner_tokens.iter().peekable(), - dcx: self.dcx, - } - .value() - } - Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token), - _ => None, - } - } - - /// parses one element on the inside of a list attribute like `#[my_attr( )]` - /// - /// parses a path followed be either: - /// 1. nothing (a word attr) - /// 2. a parenthesized list - /// 3. an equals sign and a literal (name-value) - /// - /// Can also parse *just* a literal. This is for cases like as `#[my_attr("literal")]` - /// where no path is given before the literal - /// - /// Some exceptions too for interpolated attributes which are already pre-processed - fn next(&mut self) -> Option> { - // a list element is either a literal - if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek() - && let Some(lit) = MetaItemLit::from_token(token) - { - self.inside_delimiters.next(); - return Some(MetaItemOrLitParser::Lit(lit)); - } else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) = - self.inside_delimiters.peek() - { - self.inside_delimiters.next(); - return MetaItemListParserContext { - inside_delimiters: inner_tokens.iter().peekable(), - dcx: self.dcx, - } - .next(); + if !lit.kind.is_unsuffixed() { + // Emit error and continue, we can still parse the attribute as if the suffix isn't there + self.should_emit.emit_err( + self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), + ); } - // or a path. - let path = self.next_path()?; - - // Paths can be followed by: - // - `(more meta items)` (another list) - // - `= lit` (a name-value) - // - nothing - Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() { - Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => { - self.inside_delimiters.next(); - - MetaItemParser { - path: PathParser::Attr(path), - args: ArgParser::List(MetaItemListParser::new_tts( - inner_tokens.iter(), - dspan.entire(), - self.dcx, - )), - } - } - Some(TokenTree::Delimited(_, ..)) => { - self.inside_delimiters.next(); - // self.dcx.span_delayed_bug(span.entire(), "wrong delimiters"); - return None; - } - Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => { - self.inside_delimiters.next(); - let value = self.value()?; - MetaItemParser { - path: PathParser::Attr(path), - args: ArgParser::NameValue(NameValueParser { - eq_span: *span, - value_span: value.span, - value, - }), - } - } - _ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs }, - })) + Ok(lit) } - fn parse(mut self, span: Span) -> MetaItemListParser<'a> { - let mut sub_parsers = Vec::new(); - - while !self.done() { - let Some(n) = self.next() else { - continue; + fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> { + if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() { + return if has_meta_form { + let attr_item = self + .parser + .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| { + MetaItemListParserContext { parser: this, should_emit: self.should_emit } + .parse_attr_item() + }) + .unwrap(); + Ok(attr_item) + } else { + self.parser.unexpected_any() }; - sub_parsers.push(n); + } - match self.inside_delimiters.peek() { - None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => { - self.inside_delimiters.next(); - } - Some(_) => {} + let path = self.parser.parse_path(PathStyle::Mod)?; + + // Check style of arguments that this meta item has + let args = if self.parser.check(exp!(OpenParen)) { + let start = self.parser.token.span; + let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| { + MetaItemListParserContext { parser, should_emit: self.should_emit } + .parse_meta_item_inner() + })?; + let end = self.parser.prev_token.span; + ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) }) + } else if self.parser.eat(exp!(Eq)) { + let eq_span = self.parser.prev_token.span; + let value = self.parse_unsuffixed_meta_item_lit()?; + + ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span }) + } else { + ArgParser::NoArgs + }; + + Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args }) + } + + fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> { + match self.parse_unsuffixed_meta_item_lit() { + Ok(lit) => return Ok(MetaItemOrLitParser::Lit(lit)), + Err(err) => err.cancel(), // we provide a better error below + } + + match self.parse_attr_item() { + Ok(mi) => return Ok(MetaItemOrLitParser::MetaItemParser(mi)), + Err(err) => err.cancel(), // we provide a better error below + } + + let mut err = InvalidMetaItem { + span: self.parser.token.span, + descr: token_descr(&self.parser.token), + quote_ident_sugg: None, + remove_neg_sugg: None, + }; + + // Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and + // don't `uninterpolate` the token to avoid suggesting anything butchered or questionable + // when macro metavariables are involved. + if self.parser.prev_token == token::Eq + && let token::Ident(..) = self.parser.token.kind + { + let before = self.parser.token.span.shrink_to_lo(); + while let token::Ident(..) = self.parser.token.kind { + self.parser.bump(); + } + err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg { + before, + after: self.parser.prev_token.span.shrink_to_hi(), + }); + } + + if self.parser.token == token::Minus + && self + .parser + .look_ahead(1, |t| matches!(t.kind, rustc_ast::token::TokenKind::Literal { .. })) + { + err.remove_neg_sugg = + Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span }); + self.parser.bump(); + self.parser.bump(); + } + + Err(self.parser.dcx().create_err(err)) + } + + fn parse( + tokens: TokenStream, + psess: &'sess ParseSess, + span: Span, + should_emit: ShouldEmit, + ) -> PResult<'sess, MetaItemListParser<'static>> { + let mut parser = Parser::new(psess, tokens, None); + let mut this = MetaItemListParserContext { parser: &mut parser, should_emit }; + + // Presumably, the majority of the time there will only be one attr. + let mut sub_parsers = ThinVec::with_capacity(1); + while this.parser.token != token::Eof { + sub_parsers.push(this.parse_meta_item_inner()?); + + if !this.parser.eat(exp!(Comma)) { + break; } } - MetaItemListParser { sub_parsers, span } + if parser.token != token::Eof { + parser.unexpected()?; + } + + Ok(MetaItemListParser { sub_parsers, span }) } } #[derive(Debug, Clone)] pub struct MetaItemListParser<'a> { - sub_parsers: Vec>, + sub_parsers: ThinVec>, pub span: Span, } impl<'a> MetaItemListParser<'a> { - fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self { - MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx) - } - - fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self { - MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span) + fn new<'sess>( + delim: &'a DelimArgs, + psess: &'sess ParseSess, + should_emit: ShouldEmit, + ) -> Option { + match MetaItemListParserContext::parse( + delim.tokens.clone(), + psess, + delim.dspan.entire(), + should_emit, + ) { + Ok(s) => Some(s), + Err(e) => { + should_emit.emit_err(e); + None + } + } } /// Lets you pick and choose as what you want to parse each element in the list diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 95e85667cd66..72bee0ddfbfe 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1,6 +1,6 @@ use std::num::IntErrorKind; -use rustc_ast as ast; +use rustc_ast::{self as ast, AttrStyle, Path}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, @@ -485,10 +485,12 @@ pub(crate) struct EmptyAttributeList { #[warning] #[help] pub(crate) struct InvalidTargetLint { - pub name: Symbol, + pub name: AttrPath, pub target: &'static str, - pub applied: String, + pub applied: DiagArgValue, pub only: &'static str, + #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + pub attr_span: Span, } #[derive(Diagnostic)] @@ -496,10 +498,11 @@ pub(crate) struct InvalidTargetLint { #[diag(attr_parsing_invalid_target)] pub(crate) struct InvalidTarget { #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] pub span: Span, - pub name: Symbol, + pub name: AttrPath, pub target: &'static str, - pub applied: String, + pub applied: DiagArgValue, pub only: &'static str, } @@ -555,7 +558,7 @@ pub(crate) struct LinkOrdinalOutOfRange { pub ordinal: u128, } -pub(crate) enum AttributeParseErrorReason { +pub(crate) enum AttributeParseErrorReason<'a> { ExpectedNoArgs, ExpectedStringLiteral { byte_string: Option, @@ -568,7 +571,7 @@ pub(crate) enum AttributeParseErrorReason { ExpectedNameValue(Option), DuplicateKey(Symbol), ExpectedSpecificArgument { - possibilities: Vec<&'static str>, + possibilities: &'a [Symbol], strings: bool, /// Should we tell the user to write a list when they didn't? list: bool, @@ -576,15 +579,16 @@ pub(crate) enum AttributeParseErrorReason { ExpectedIdentifier, } -pub(crate) struct AttributeParseError { +pub(crate) struct AttributeParseError<'a> { pub(crate) span: Span, pub(crate) attr_span: Span, + pub(crate) attr_style: AttrStyle, pub(crate) template: AttributeTemplate, pub(crate) attribute: AttrPath, - pub(crate) reason: AttributeParseErrorReason, + pub(crate) reason: AttributeParseErrorReason<'a>, } -impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { +impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let name = self.attribute.to_string(); @@ -653,7 +657,7 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { list: false, } => { let quote = if strings { '"' } else { '`' }; - match possibilities.as_slice() { + match possibilities { &[] => {} &[x] => { diag.span_label( @@ -683,7 +687,7 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { list: true, } => { let quote = if strings { '"' } else { '`' }; - match possibilities.as_slice() { + match possibilities { &[] => {} &[x] => { diag.span_label( @@ -717,7 +721,8 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { if let Some(link) = self.template.docs { diag.note(format!("for more information, visit <{link}>")); } - let suggestions = self.template.suggestions(false, &name); + let suggestions = self.template.suggestions(self.attr_style, &name); + diag.span_suggestions( self.attr_span, if suggestions.len() == 1 { @@ -732,3 +737,92 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { diag } } + +#[derive(Diagnostic)] +#[diag(attr_parsing_invalid_attr_unsafe)] +#[note] +pub(crate) struct InvalidAttrUnsafe { + #[primary_span] + #[label] + pub span: Span, + pub name: Path, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_unsafe_attr_outside_unsafe)] +pub(crate) struct UnsafeAttrOutsideUnsafe { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + attr_parsing_unsafe_attr_outside_unsafe_suggestion, + applicability = "machine-applicable" +)] +pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { + #[suggestion_part(code = "unsafe(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_meta_bad_delim)] +pub(crate) struct MetaBadDelim { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: MetaBadDelimSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + attr_parsing_meta_bad_delim_suggestion, + applicability = "machine-applicable" +)] +pub(crate) struct MetaBadDelimSugg { + #[suggestion_part(code = "(")] + pub open: Span, + #[suggestion_part(code = ")")] + pub close: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_invalid_meta_item)] +pub(crate) struct InvalidMetaItem { + #[primary_span] + pub span: Span, + pub descr: String, + #[subdiagnostic] + pub quote_ident_sugg: Option, + #[subdiagnostic] + pub remove_neg_sugg: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(attr_parsing_quote_ident_sugg, applicability = "machine-applicable")] +pub(crate) struct InvalidMetaItemQuoteIdentSugg { + #[suggestion_part(code = "\"")] + pub before: Span, + #[suggestion_part(code = "\"")] + pub after: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(attr_parsing_remove_neg_sugg, applicability = "machine-applicable")] +pub(crate) struct InvalidMetaItemRemoveNegSugg { + #[suggestion_part(code = "")] + pub negative_sign: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_suffixed_literal_in_attribute)] +#[help] +pub(crate) struct SuffixedLiteralInAttribute { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs new file mode 100644 index 000000000000..9568b791b3f8 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -0,0 +1,247 @@ +use std::borrow::Cow; + +use rustc_errors::DiagArgValue; +use rustc_feature::Features; +use rustc_hir::lints::{AttributeLint, AttributeLintKind}; +use rustc_hir::{AttrPath, MethodKind, Target}; +use rustc_span::Span; + +use crate::AttributeParser; +use crate::context::Stage; +use crate::session_diagnostics::InvalidTarget; + +#[derive(Debug)] +pub(crate) enum AllowedTargets { + AllowList(&'static [Policy]), + AllowListWarnRest(&'static [Policy]), +} + +pub(crate) enum AllowedResult { + Allowed, + Warn, + Error, +} + +impl AllowedTargets { + pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult { + match self { + AllowedTargets::AllowList(list) => { + if list.contains(&Policy::Allow(target)) { + AllowedResult::Allowed + } else if list.contains(&Policy::Warn(target)) { + AllowedResult::Warn + } else { + AllowedResult::Error + } + } + AllowedTargets::AllowListWarnRest(list) => { + if list.contains(&Policy::Allow(target)) { + AllowedResult::Allowed + } else if list.contains(&Policy::Error(target)) { + AllowedResult::Error + } else { + AllowedResult::Warn + } + } + } + } + + pub(crate) fn allowed_targets(&self) -> Vec { + match self { + AllowedTargets::AllowList(list) => list, + AllowedTargets::AllowListWarnRest(list) => list, + } + .iter() + .filter_map(|target| match target { + Policy::Allow(target) => Some(*target), + Policy::Warn(_) => None, + Policy::Error(_) => None, + }) + .collect() + } +} + +#[derive(Debug, Eq, PartialEq)] +pub(crate) enum Policy { + Allow(Target), + Warn(Target), + Error(Target), +} + +impl AttributeParser<'_, S> { + pub(crate) fn check_target( + &self, + attr_name: AttrPath, + attr_span: Span, + allowed_targets: &AllowedTargets, + target: Target, + target_id: S::Id, + mut emit_lint: impl FnMut(AttributeLint), + ) { + match allowed_targets.is_allowed(target) { + AllowedResult::Allowed => {} + AllowedResult::Warn => { + let allowed_targets = allowed_targets.allowed_targets(); + let (applied, only) = + allowed_targets_applied(allowed_targets, target, self.features); + emit_lint(AttributeLint { + id: target_id, + span: attr_span, + kind: AttributeLintKind::InvalidTarget { + name: attr_name, + target, + only: if only { "only " } else { "" }, + applied, + }, + }); + } + AllowedResult::Error => { + let allowed_targets = allowed_targets.allowed_targets(); + let (applied, only) = + allowed_targets_applied(allowed_targets, target, self.features); + self.dcx().emit_err(InvalidTarget { + span: attr_span, + name: attr_name, + target: target.plural_name(), + only: if only { "only " } else { "" }, + applied: DiagArgValue::StrListSepByAnd( + applied.into_iter().map(Cow::Owned).collect(), + ), + }); + } + } + } +} + +/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to. +/// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string +pub(crate) fn allowed_targets_applied( + mut allowed_targets: Vec, + target: Target, + features: Option<&Features>, +) -> (Vec, bool) { + // Remove unstable targets from `allowed_targets` if their features are not enabled + if let Some(features) = features { + if !features.fn_delegation() { + allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. })); + } + if !features.stmt_expr_attributes() { + allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement)); + } + if !features.extern_types() { + allowed_targets.retain(|t| !matches!(t, Target::ForeignTy)); + } + } + + // We define groups of "similar" targets. + // If at least two of the targets are allowed, and the `target` is not in the group, + // we collapse the entire group to a single entry to simplify the target list + const FUNCTION_LIKE: &[Target] = &[ + Target::Fn, + Target::Closure, + Target::ForeignFn, + Target::Method(MethodKind::Inherent), + Target::Method(MethodKind::Trait { body: false }), + Target::Method(MethodKind::Trait { body: true }), + Target::Method(MethodKind::TraitImpl), + ]; + const METHOD_LIKE: &[Target] = &[ + Target::Method(MethodKind::Inherent), + Target::Method(MethodKind::Trait { body: false }), + Target::Method(MethodKind::Trait { body: true }), + Target::Method(MethodKind::TraitImpl), + ]; + const IMPL_LIKE: &[Target] = + &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }]; + const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum]; + + let mut added_fake_targets = Vec::new(); + filter_targets( + &mut allowed_targets, + FUNCTION_LIKE, + "functions", + target, + &mut added_fake_targets, + ); + filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets); + filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets); + filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets); + + // If there is now only 1 target left, show that as the only possible target + ( + added_fake_targets + .iter() + .copied() + .chain(allowed_targets.iter().map(|t| t.plural_name())) + .map(|i| i.to_string()) + .collect(), + allowed_targets.len() + added_fake_targets.len() == 1, + ) +} + +fn filter_targets( + allowed_targets: &mut Vec, + target_group: &'static [Target], + target_group_name: &'static str, + target: Target, + added_fake_targets: &mut Vec<&'static str>, +) { + if target_group.contains(&target) { + return; + } + if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 { + return; + } + allowed_targets.retain(|t| !target_group.contains(t)); + added_fake_targets.push(target_group_name); +} + +/// This is the list of all targets to which a attribute can be applied +/// This is used for: +/// - `rustc_dummy`, which can be applied to all targets +/// - Attributes that are not parted to the new target system yet can use this list as a placeholder +pub(crate) const ALL_TARGETS: &'static [Policy] = { + use Policy::Allow; + &[ + Allow(Target::ExternCrate), + Allow(Target::Use), + Allow(Target::Static), + Allow(Target::Const), + Allow(Target::Fn), + Allow(Target::Closure), + Allow(Target::Mod), + Allow(Target::ForeignMod), + Allow(Target::GlobalAsm), + Allow(Target::TyAlias), + Allow(Target::Enum), + Allow(Target::Variant), + Allow(Target::Struct), + Allow(Target::Field), + Allow(Target::Union), + Allow(Target::Trait), + Allow(Target::TraitAlias), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::Expression), + Allow(Target::Statement), + Allow(Target::Arm), + Allow(Target::AssocConst), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::AssocTy), + Allow(Target::ForeignFn), + Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), + Allow(Target::MacroDef), + Allow(Target::Param), + Allow(Target::PatField), + Allow(Target::ExprField), + Allow(Target::WherePredicate), + Allow(Target::MacroCall), + Allow(Target::Crate), + Allow(Target::Delegation { mac: false }), + Allow(Target::Delegation { mac: true }), + ] +}; diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs similarity index 95% rename from compiler/rustc_parse/src/validate_attr.rs rename to compiler/rustc_attr_parsing/src/validate_attr.rs index 68ef6d6f32cb..7a7624893bd2 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -8,16 +8,16 @@ self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId, Path, Safety, }; -use rustc_attr_parsing::{AttributeParser, Late}; use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult}; use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; +use rustc_parse::parse_in; use rustc_session::errors::report_lit_error; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE}; use rustc_session::parse::ParseSess; use rustc_span::{Span, Symbol, sym}; -use crate::{errors, parse_in}; +use crate::{AttributeParser, Late, session_diagnostics as errors}; pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) { if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) @@ -33,7 +33,10 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) { // Check input tokens for built-in and key-value attributes. match builtin_attr_info { // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. - Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => { + Some(BuiltinAttribute { name, template, .. }) => { + if AttributeParser::::is_parsed_attribute(slice::from_ref(&name)) { + return; + } match parse_meta(psess, attr) { // Don't check safety again, we just did that Ok(meta) => { @@ -133,16 +136,6 @@ fn check_meta_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 { - span: span.entire(), - sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, - }); -} - /// Checks that the given meta-item is compatible with this `AttributeTemplate`. fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool { let is_one_allowed_subword = |items: &[MetaItemInner]| match items { @@ -269,9 +262,6 @@ pub fn check_builtin_meta_item( ) { if !is_attr_template_compatible(&template, &meta.kind) { // attrs with new parsers are locally validated so excluded here - if AttributeParser::::is_parsed_attribute(slice::from_ref(&name)) { - return; - } emit_malformed_attribute(psess, style, meta.span, name, template); } diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index 33b80c4b03d6..f59e106c7ac3 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -90,7 +90,7 @@ borrowck_lifetime_constraints_error = lifetime may not live long enough borrowck_limitations_implies_static = - due to current limitations in the borrow checker, this implies a `'static` lifetime + due to a current limitation of the type system, this implies a `'static` lifetime borrowck_move_closure_suggestion = consider adding 'move' keyword before the nested closure diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 57db2e9fb574..d3f6c01ab8c3 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -1,7 +1,6 @@ use std::fmt; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::graph; use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_middle::mir::{ self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges, @@ -317,9 +316,8 @@ fn compute( loans_out_of_scope_at_location: FxIndexMap::default(), }; for (loan_idx, loan_data) in borrow_set.iter_enumerated() { - let issuing_region = loan_data.region; let loan_issued_at = loan_data.reserve_location; - prec.precompute_loans_out_of_scope(loan_idx, issuing_region, loan_issued_at); + prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at); } prec.loans_out_of_scope_at_location @@ -328,45 +326,7 @@ fn compute( /// Loans are in scope while they are live: whether they are contained within any live region. /// In the location-insensitive analysis, a loan will be contained in a region if the issuing /// region can reach it in the subset graph. So this is a reachability problem. - fn precompute_loans_out_of_scope( - &mut self, - loan_idx: BorrowIndex, - issuing_region: RegionVid, - loan_issued_at: Location, - ) { - let sccs = self.regioncx.constraint_sccs(); - let universal_regions = self.regioncx.universal_regions(); - - // The loop below was useful for the location-insensitive analysis but shouldn't be - // impactful in the location-sensitive case. It seems that it does, however, as without it a - // handful of tests fail. That likely means some liveness or outlives data related to choice - // regions is missing - // FIXME: investigate the impact of loans traversing applied member constraints and why some - // tests fail otherwise. - // - // We first handle the cases where the loan doesn't go out of scope, depending on the - // issuing region's successors. - for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) { - // Via applied member constraints - // - // The issuing region can flow into the choice regions, and they are either: - // - placeholders or free regions themselves, - // - or also transitively outlive a free region. - // - // That is to say, if there are applied member constraints here, the loan escapes the - // function and cannot go out of scope. We could early return here. - // - // For additional insurance via fuzzing and crater, we verify that the constraint's min - // choice indeed escapes the function. In the future, we could e.g. turn this check into - // a debug assert and early return as an optimization. - let scc = sccs.scc(successor); - for constraint in self.regioncx.applied_member_constraints(scc) { - if universal_regions.is_universal_region(constraint.min_choice) { - return; - } - } - } - + fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) { let first_block = loan_issued_at.block; let first_bb_data = &self.body.basic_blocks[first_block]; diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index b67dba3af96d..5642cdf87fde 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -6,7 +6,9 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify}; use rustc_hir::def::{CtorKind, Namespace}; -use rustc_hir::{self as hir, CoroutineKind, LangItem}; +use rustc_hir::{ + self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind, +}; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin}; use rustc_infer::traits::SelectionError; @@ -658,25 +660,66 @@ pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> Str /// Add a note to region errors and borrow explanations when higher-ranked regions in predicates /// implicitly introduce an "outlives `'static`" constraint. + /// + /// This is very similar to `fn suggest_static_lifetime_for_gat_from_hrtb` which handles this + /// note for failed type tests instead of outlives errors. fn add_placeholder_from_predicate_note( &self, - err: &mut Diag<'_, G>, + diag: &mut Diag<'_, G>, path: &[OutlivesConstraint<'tcx>], ) { - let predicate_span = path.iter().find_map(|constraint| { + let tcx = self.infcx.tcx; + let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| { let outlived = constraint.sub; if let Some(origin) = self.regioncx.definitions.get(outlived) - && let NllRegionVariableOrigin::Placeholder(_) = origin.origin - && let ConstraintCategory::Predicate(span) = constraint.category + && let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin + && let Some(id) = placeholder.bound.kind.get_id() + && let Some(placeholder_id) = id.as_local() + && let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id) + && let Some(generics_impl) = + tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics() { - Some(span) + Some((gat_hir_id, generics_impl)) } else { None } - }); + }) else { + return; + }; - if let Some(span) = predicate_span { - err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); + // Look for the where-bound which introduces the placeholder. + // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>` + // and `T: for<'a> Trait`<'a>. + for pred in generics.predicates { + let WherePredicateKind::BoundPredicate(WhereBoundPredicate { + bound_generic_params, + bounds, + .. + }) = pred.kind + else { + continue; + }; + if bound_generic_params + .iter() + .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) + .is_some() + { + diag.span_note(pred.span, fluent::borrowck_limitations_implies_static); + return; + } + for bound in bounds.iter() { + if let GenericBound::Trait(bound) = bound { + if bound + .bound_generic_params + .iter() + .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) + .is_some() + { + diag.span_note(bound.span, fluent::borrowck_limitations_implies_static); + return; + } + } + } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index c0ca35f9ff83..ea264c8064ad 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -3,6 +3,7 @@ use core::ops::ControlFlow; +use either::Either; use hir::{ExprKind, Param}; use rustc_abi::FieldIdx; use rustc_errors::{Applicability, Diag}; @@ -12,15 +13,16 @@ use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::visit::PlaceContext; use rustc_middle::mir::{ - self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place, - PlaceRef, ProjectionElem, + self, BindingForm, Body, BorrowKind, Local, LocalDecl, LocalInfo, LocalKind, Location, + Mutability, Operand, Place, PlaceRef, ProjectionElem, RawPtrKind, Rvalue, Statement, + StatementKind, TerminatorKind, }; use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast}; use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; -use tracing::debug; +use tracing::{debug, trace}; use crate::diagnostics::BorrowedContentSource; use crate::{MirBorrowckCtxt, session_diagnostics}; @@ -31,6 +33,33 @@ pub(crate) enum AccessKind { Mutate, } +/// Finds all statements that assign directly to local (i.e., X = ...) and returns their +/// locations. +fn find_assignments(body: &Body<'_>, local: Local) -> Vec { + use rustc_middle::mir::visit::Visitor; + + struct FindLocalAssignmentVisitor { + needle: Local, + locations: Vec, + } + + impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor { + fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) { + if self.needle != local { + return; + } + + if place_context.is_place_assignment() { + self.locations.push(location); + } + } + } + + let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] }; + visitor.visit_body(body); + visitor.locations +} + impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn report_mutability_error( &mut self, @@ -384,7 +413,7 @@ pub(crate) fn report_mutability_error( } } - // Also suggest adding mut for upvars + // Also suggest adding mut for upvars. PlaceRef { local, projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)], @@ -438,9 +467,8 @@ pub(crate) fn report_mutability_error( } } - // complete hack to approximate old AST-borrowck - // diagnostic: if the span starts with a mutable borrow of - // a local variable, then just suggest the user remove it. + // Complete hack to approximate old AST-borrowck diagnostic: if the span starts + // with a mutable borrow of a local variable, then just suggest the user remove it. PlaceRef { local: _, projection: [] } if self .infcx @@ -769,7 +797,7 @@ fn construct_mut_suggestion_for_local_binding_patterns( ); } - // point to span of upvar making closure call require mutable borrow + // Point to span of upvar making closure call that requires a mutable borrow fn show_mutating_upvar( &self, tcx: TyCtxt<'_>, @@ -825,7 +853,7 @@ fn show_mutating_upvar( } else { bug!("not an upvar") }; - // sometimes we deliberately don't store the name of a place when coming from a macro in + // Sometimes we deliberately don't store the name of a place when coming from a macro in // another crate. We generally want to limit those diagnostics a little, to hide // implementation details (such as those from pin!() or format!()). In that case show a // slightly different error message, or none at all if something else happened. In other @@ -936,8 +964,8 @@ fn expected_fn_found_fn_mut_call(&self, err: &mut Diag<'_>, sp: Span, act: &str) let def_id = tcx.hir_enclosing_body_owner(fn_call_id); let mut look_at_return = true; - // 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 + // If the HIR node is a function or method call, get the DefId + // of the callee function or method, 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; @@ -1051,7 +1079,7 @@ fn suggest_using_iter_mut(&self, err: &mut Diag<'_>) { let mut cur_expr = expr; while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind { if path_segment.ident.name == sym::iter { - // check `_ty` has `iter_mut` method + // Check that the type has an `iter_mut` method. let res = self .infcx .tcx @@ -1081,38 +1109,6 @@ fn suggest_using_iter_mut(&self, err: &mut Diag<'_>) { } } - /// Finds all statements that assign directly to local (i.e., X = ...) and returns their - /// locations. - fn find_assignments(&self, local: Local) -> Vec { - use rustc_middle::mir::visit::Visitor; - - struct FindLocalAssignmentVisitor { - needle: Local, - locations: Vec, - } - - impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor { - fn visit_local( - &mut self, - local: Local, - place_context: PlaceContext, - location: Location, - ) { - if self.needle != local { - return; - } - - if place_context.is_place_assignment() { - self.locations.push(location); - } - } - } - - let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] }; - visitor.visit_body(self.body); - visitor.locations - } - fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) { let local_decl = &self.body.local_decls[local]; @@ -1122,7 +1118,7 @@ fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local); if is_trait_sig && !is_local { - // Do not suggest to change the signature when the trait comes from another crate. + // Do not suggest changing the signature when the trait comes from another crate. err.span_label( local_decl.source_info.span, format!("this is an immutable {pointer_desc}"), @@ -1131,11 +1127,11 @@ fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) } let decl_span = local_decl.source_info.span; - let amp_mut_sugg = match *local_decl.local_info() { + let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() { LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => { let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span); let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span)); - Some(AmpMutSugg { has_sugg: true, span, suggestion, additional }) + (AmpMutSugg::Type { span, suggestion, additional }, None) } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { @@ -1143,79 +1139,54 @@ fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) opt_ty_info, .. })) => { - // check if the RHS is from desugaring + // Check if the RHS is from desugaring. + let first_assignment = find_assignments(&self.body, local).first().copied(); + let first_assignment_stmt = first_assignment + .and_then(|loc| self.body[loc.block].statements.get(loc.statement_index)); + trace!(?first_assignment_stmt); let opt_assignment_rhs_span = - self.find_assignments(local).first().map(|&location| { - if let Some(mir::Statement { - source_info: _, - kind: - mir::StatementKind::Assign(box ( - _, - mir::Rvalue::Use(mir::Operand::Copy(place)), - )), - .. - }) = self.body[location.block].statements.get(location.statement_index) - { - self.body.local_decls[place.local].source_info.span - } else { - self.body.source_info(location).span - } - }); - match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) { - // on for loops, RHS points to the iterator part - Some(DesugaringKind::ForLoop) => { - let span = opt_assignment_rhs_span.unwrap(); - self.suggest_similar_mut_method_for_for_loop(err, span); + first_assignment.map(|loc| self.body.source_info(loc).span); + let mut source_span = opt_assignment_rhs_span; + if let Some(mir::Statement { + source_info: _, + kind: + mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))), + .. + }) = first_assignment_stmt + { + let local_span = self.body.local_decls[place.local].source_info.span; + // `&self` in async functions have a `desugaring_kind`, but the local we assign + // it with does not, so use the local_span for our checks later. + source_span = Some(local_span); + if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() { + // On for loops, RHS points to the iterator part. + self.suggest_similar_mut_method_for_for_loop(err, local_span); err.span_label( - span, + local_span, format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",), ); - None - } - // don't create labels for compiler-generated spans - Some(_) => None, - // don't create labels for the span not from user's code - None if opt_assignment_rhs_span - .is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) => - { - None - } - None => { - if name != kw::SelfLower { - suggest_ampmut( - self.infcx.tcx, - local_decl.ty, - decl_span, - opt_assignment_rhs_span, - opt_ty_info, - ) - } else { - match local_decl.local_info() { - LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { - opt_ty_info: None, - .. - })) => { - let (span, sugg) = - suggest_ampmut_self(self.infcx.tcx, decl_span); - Some(AmpMutSugg { - has_sugg: true, - span, - suggestion: sugg, - additional: None, - }) - } - // explicit self (eg `self: &'a Self`) - _ => suggest_ampmut( - self.infcx.tcx, - local_decl.ty, - decl_span, - opt_assignment_rhs_span, - opt_ty_info, - ), - } - } + return; } } + + // Don't create labels for compiler-generated spans or spans not from users' code. + if source_span.is_some_and(|s| { + s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s) + }) { + return; + } + + // This could be because we're in an `async fn`. + if name == kw::SelfLower && opt_ty_info.is_none() { + let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span); + (AmpMutSugg::Type { span, suggestion, additional: None }, None) + } else if let Some(sugg) = + suggest_ampmut(self.infcx, self.body(), first_assignment_stmt) + { + (sugg, opt_ty_info) + } else { + return; + } } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { @@ -1223,181 +1194,238 @@ fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) .. })) => { let pattern_span: Span = local_decl.source_info.span; - suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg { - has_sugg: true, - span, - suggestion: "mut ".to_owned(), - additional: None, - }) + let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else { + return; + }; + (AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None) } _ => unreachable!(), }; - match amp_mut_sugg { - Some(AmpMutSugg { - has_sugg: true, - span: err_help_span, - suggestion: suggested_code, - additional, - }) => { - let mut sugg = vec![(err_help_span, suggested_code)]; - if let Some(s) = additional { - sugg.push(s); - } + let mut suggest = |suggs: Vec<_>, applicability, extra| { + if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) { + return; + } - if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span)) - { - err.multipart_suggestion_verbose( - format!( - "consider changing this to be a mutable {pointer_desc}{}", - if is_trait_sig { - " in the `impl` method and the `trait` definition" - } else { - "" - } - ), - sugg, - Applicability::MachineApplicable, + err.multipart_suggestion_verbose( + format!( + "consider changing this to be a mutable {pointer_desc}{}{extra}", + if is_trait_sig { + " in the `impl` method and the `trait` definition" + } else { + "" + } + ), + suggs, + applicability, + ); + }; + + let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg { + AmpMutSugg::Type { span, suggestion, additional } => { + let mut sugg = vec![(span, suggestion)]; + sugg.extend(additional); + suggest(sugg, Applicability::MachineApplicable, ""); + return; + } + AmpMutSugg::MapGetMut { span, suggestion } => { + if self.infcx.tcx.sess.source_map().is_imported(span) { + return; + } + err.multipart_suggestion_verbose( + "consider using `get_mut`", + vec![(span, suggestion)], + Applicability::MaybeIncorrect, + ); + return; + } + AmpMutSugg::Expr { span, suggestion } => { + // `Expr` suggestions should change type annotations if they already exist (probably immut), + // but do not add new type annotations. + (vec![(span, suggestion)], false) + } + AmpMutSugg::ChangeBinding => (vec![], true), + }; + + // Find a binding's type to make mutable. + let (binding_exists, span) = match local_var_ty_info { + // If this is a variable binding with an explicit type, + // then we will suggest changing it to be mutable. + // This is `Applicability::MachineApplicable`. + Some(ty_span) => (true, ty_span), + + // Otherwise, we'll suggest *adding* an annotated type, we'll suggest + // the RHS's type for that. + // This is `Applicability::HasPlaceholders`. + None => (false, decl_span), + }; + + if !binding_exists && !add_type_annotation_if_not_exists { + suggest(sugg, Applicability::MachineApplicable, ""); + return; + } + + // If the binding already exists and is a reference with an explicit + // lifetime, then we can suggest adding ` mut`. This is special-cased from + // the path without an explicit lifetime. + let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span) + && src.starts_with("&'") + // Note that `&' a T` is invalid so this is correct. + && let Some(ws_pos) = src.find(char::is_whitespace) + { + let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo(); + (span, " mut".to_owned(), true) + // If there is already a binding, we modify it to be `mut`. + } else if binding_exists { + // Shrink the span to just after the `&` in `&variable`. + let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo(); + (span, "mut ".to_owned(), true) + } else { + // Otherwise, suggest that the user annotates the binding; We provide the + // type of the local. + let ty = local_decl.ty.builtin_deref(true).unwrap(); + + (span, format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false) + }; + + if suggest_now { + // Suggest changing `&x` to `&mut x` and changing `&T` to `&mut T` at the same time. + let has_change = !sugg.is_empty(); + sugg.push((sugg_span, sugg_str)); + suggest( + sugg, + Applicability::MachineApplicable, + // FIXME(fee1-dead) this somehow doesn't fire + if has_change { " and changing the binding's type" } else { "" }, + ); + return; + } else if !sugg.is_empty() { + suggest(sugg, Applicability::MachineApplicable, ""); + return; + } + + let def_id = self.body.source.def_id(); + let hir_id = if let Some(local_def_id) = def_id.as_local() + && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) + { + BindingFinder { span: sugg_span }.visit_body(&body).break_value() + } else { + None + }; + let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id)); + + let Some(hir::Node::LetStmt(local)) = node else { + err.span_label( + sugg_span, + format!("consider changing this binding's type to be: `{sugg_str}`"), + ); + return; + }; + + let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap()); + if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() + && let Some(expr) = local.init + && let ty = tables.node_type_opt(expr.hir_id) + && let Some(ty) = ty + && let ty::Ref(..) = ty.kind() + { + match self + .infcx + .type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env) + .as_deref() + { + Some([]) => { + // FIXME: This error message isn't useful, since we're just + // vaguely suggesting to clone a value that already + // implements `Clone`. + // + // A correct suggestion here would take into account the fact + // that inference may be affected by missing types on bindings, + // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for + // example. + } + None => { + if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind + && segment.ident.name == sym::clone + { + err.span_help( + span, + format!( + "`{}` doesn't implement `Clone`, so this call clones \ + the reference `{ty}`", + ty.peel_refs(), + ), + ); + } + // The type doesn't implement Clone. + let trait_ref = ty::Binder::dummy(ty::TraitRef::new( + self.infcx.tcx, + clone_trait, + [ty.peel_refs()], + )); + let obligation = traits::Obligation::new( + self.infcx.tcx, + traits::ObligationCause::dummy(), + self.infcx.param_env, + trait_ref, + ); + self.infcx.err_ctxt().suggest_derive( + &obligation, + err, + trait_ref.upcast(self.infcx.tcx), ); } - } - Some(AmpMutSugg { - has_sugg: false, span: err_label_span, suggestion: 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) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) - { - BindingFinder { span: err_label_span }.visit_body(&body).break_value() - } else { - None - }; - - if let Some(hir_id) = hir_id - && let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id) - { - let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap()); - if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() - && let Some(expr) = local.init - && let ty = tables.node_type_opt(expr.hir_id) - && let Some(ty) = ty - && let ty::Ref(..) = ty.kind() + Some(errors) => { + if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind + && segment.ident.name == sym::clone { - match self - .infcx - .type_implements_trait_shallow( - clone_trait, - ty.peel_refs(), - self.infcx.param_env, - ) - .as_deref() - { - Some([]) => { - // FIXME: This error message isn't useful, since we're just - // vaguely suggesting to clone a value that already - // implements `Clone`. - // - // A correct suggestion here would take into account the fact - // that inference may be affected by missing types on bindings, - // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for - // example. - } - None => { - if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = - expr.kind - && segment.ident.name == sym::clone - { - err.span_help( - span, - format!( - "`{}` doesn't implement `Clone`, so this call clones \ - the reference `{ty}`", - ty.peel_refs(), - ), - ); - } - // The type doesn't implement Clone. - let trait_ref = ty::Binder::dummy(ty::TraitRef::new( - self.infcx.tcx, - clone_trait, - [ty.peel_refs()], - )); - let obligation = traits::Obligation::new( - self.infcx.tcx, - traits::ObligationCause::dummy(), - self.infcx.param_env, - trait_ref, - ); - self.infcx.err_ctxt().suggest_derive( - &obligation, - err, - trait_ref.upcast(self.infcx.tcx), - ); - } - Some(errors) => { - if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = - expr.kind - && segment.ident.name == sym::clone - { - err.span_help( - span, - format!( - "`{}` doesn't implement `Clone` because its \ + err.span_help( + span, + format!( + "`{}` doesn't implement `Clone` because its \ implementations trait bounds could not be met, so \ this call clones the reference `{ty}`", - ty.peel_refs(), - ), - ); - err.note(format!( - "the following trait bounds weren't met: {}", - errors - .iter() - .map(|e| e.obligation.predicate.to_string()) - .collect::>() - .join("\n"), - )); - } - // The type doesn't implement Clone because of unmet obligations. - for error in errors { - if let traits::FulfillmentErrorCode::Select( - traits::SelectionError::Unimplemented, - ) = error.code - && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( - pred, - )) = error.obligation.predicate.kind().skip_binder() - { - self.infcx.err_ctxt().suggest_derive( - &error.obligation, - err, - error.obligation.predicate.kind().rebind(pred), - ); - } - } - } + ty.peel_refs(), + ), + ); + err.note(format!( + "the following trait bounds weren't met: {}", + errors + .iter() + .map(|e| e.obligation.predicate.to_string()) + .collect::>() + .join("\n"), + )); + } + // The type doesn't implement Clone because of unmet obligations. + for error in errors { + if let traits::FulfillmentErrorCode::Select( + traits::SelectionError::Unimplemented, + ) = error.code + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = + error.obligation.predicate.kind().skip_binder() + { + self.infcx.err_ctxt().suggest_derive( + &error.obligation, + err, + error.obligation.predicate.kind().rebind(pred), + ); } } - let (changing, span, sugg) = match local.ty { - Some(ty) => ("changing", ty.span, message), - None => { - ("specifying", local.pat.span.shrink_to_hi(), format!(": {message}")) - } - }; - err.span_suggestion_verbose( - span, - format!("consider {changing} this binding's type"), - sugg, - Applicability::HasPlaceholders, - ); - } else { - err.span_label( - err_label_span, - format!("consider changing this binding's type to be: `{message}`"), - ); } } - None => {} } + let (changing, span, sugg) = match local.ty { + Some(ty) => ("changing", ty.span, sugg_str), + None => ("specifying", local.pat.span.shrink_to_hi(), format!(": {sugg_str}")), + }; + err.span_suggestion_verbose( + span, + format!("consider {changing} this binding's type"), + sugg, + Applicability::HasPlaceholders, + ); } } @@ -1464,11 +1492,25 @@ fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) { } } -struct AmpMutSugg { - has_sugg: bool, - span: Span, - suggestion: String, - additional: Option<(Span, String)>, +enum AmpMutSugg { + /// Type suggestion. Changes `&self` to `&mut self`, `x: &T` to `x: &mut T`, + /// `ref x` to `ref mut x`, etc. + Type { + span: Span, + suggestion: String, + additional: Option<(Span, String)>, + }, + /// Suggestion for expressions, `&x` to `&mut x`, `&x[i]` to `&mut x[i]`, etc. + Expr { + span: Span, + suggestion: String, + }, + /// Suggests `.get_mut` in the case of `&map[&key]` for Hash/BTreeMap. + MapGetMut { + span: Span, + suggestion: String, + }, + ChangeBinding, } // When we want to suggest a user change a local variable to be a `&mut`, there @@ -1487,110 +1529,111 @@ struct AmpMutSugg { // This implementation attempts to emulate AST-borrowck prioritization // by trying (3.), then (2.) and finally falling back on (1.). fn suggest_ampmut<'tcx>( - tcx: TyCtxt<'tcx>, - decl_ty: Ty<'tcx>, - decl_span: Span, - opt_assignment_rhs_span: Option, - opt_ty_info: Option, + infcx: &crate::BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + opt_assignment_rhs_stmt: Option<&Statement<'tcx>>, ) -> Option { - // if there is a RHS and it starts with a `&` from it, then check if it is + let tcx = infcx.tcx; + // If there is a RHS and it starts with a `&` from it, then check if it is // mutable, and if not, put suggest putting `mut ` to make it mutable. - // we don't have to worry about lifetime annotations here because they are + // We don't have to worry about lifetime annotations here because they are // not valid when taking a reference. For example, the following is not valid Rust: // // let x: &i32 = &'a 5; // ^^ lifetime annotation not allowed // - if let Some(rhs_span) = opt_assignment_rhs_span - && let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span) - && let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&') + if let Some(rhs_stmt) = opt_assignment_rhs_stmt + && let StatementKind::Assign(box (lhs, rvalue)) = &rhs_stmt.kind + && let mut rhs_span = rhs_stmt.source_info.span + && let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span) { - // Suggest changing `&raw const` to `&raw mut` if applicable. - if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() { - let const_idx = rhs_str.find("const").unwrap() as u32; - let const_span = rhs_span - .with_lo(rhs_span.lo() + BytePos(const_idx)) - .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)); + let mut rvalue = rvalue; - return Some(AmpMutSugg { - has_sugg: true, - span: const_span, - suggestion: "mut".to_owned(), - additional: None, - }); - } - - // Figure out if rhs already is `&mut`. - let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") { - match rest.chars().next() { - // e.g. `&mut x` - Some(c) if c.is_whitespace() => true, - // e.g. `&mut(x)` - Some('(') => true, - // e.g. `&mut{x}` - Some('{') => true, - // e.g. `&mutablevar` - _ => false, + // Take some special care when handling `let _x = &*_y`: + // We want to know if this is part of an overloaded index, so `let x = &a[0]`, + // or whether this is a usertype ascription (`let _x: &T = y`). + if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue + && place.projection.len() == 1 + && place.projection[0] == ProjectionElem::Deref + && let Some(assign) = find_assignments(&body, place.local).first() + { + // If this is a usertype ascription (`let _x: &T = _y`) then pierce through it as either we want + // to suggest `&mut` on the expression (handled here) or we return `None` and let the caller + // suggest `&mut` on the type if the expression seems fine (e.g. `let _x: &T = &mut _y`). + if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref() + && let [user_ty_proj] = user_ty_projs.contents.as_slice() + && user_ty_proj.projs.is_empty() + && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign) + && let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind + && let rhs_span_new = rhs_stmt_new.source_info.span + && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span) + { + (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new); } - } else { - false - }; - // if the reference is already mutable then there is nothing we can do - // here. - if !is_mut { - // shrink the span to just after the `&` in `&variable` - let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo(); - // FIXME(Ezrashaw): returning is bad because we still might want to - // update the annotated type, see #106857. - return Some(AmpMutSugg { - has_sugg: true, - span, - suggestion: "mut ".to_owned(), - additional: None, - }); + if let Either::Right(call) = body.stmt_at(*assign) + && let TerminatorKind::Call { + func: Operand::Constant(box const_operand), args, .. + } = &call.kind + && let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind() + && let Some(trait_) = tcx.trait_of_assoc(method_def_id) + && tcx.is_lang_item(trait_, hir::LangItem::Index) + { + let trait_ref = ty::TraitRef::from_assoc( + tcx, + tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span), + method_args, + ); + // The type only implements `Index` but not `IndexMut`, we must not suggest `&mut`. + if !infcx + .type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env) + .must_apply_considering_regions() + { + // Suggest `get_mut` if type is a `BTreeMap` or `HashMap`. + if let ty::Adt(def, _) = trait_ref.self_ty().kind() + && [sym::BTreeMap, sym::HashMap] + .into_iter() + .any(|s| tcx.is_diagnostic_item(s, def.did())) + && let [map, key] = &**args + && let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span) + && let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span) + { + let span = rhs_span; + let suggestion = format!("{map}.get_mut({key}).unwrap()"); + return Some(AmpMutSugg::MapGetMut { span, suggestion }); + } + return None; + } + } + } + + let sugg = match rvalue { + Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => { + // Shrink the span to just after the `&` in `&variable`. + Some(( + rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(), + "mut ".to_owned(), + )) + } + Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => { + // Suggest changing `&raw const` to `&raw mut` if applicable. + let const_idx = const_idx as u32; + Some(( + rhs_span + .with_lo(rhs_span.lo() + BytePos(const_idx)) + .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)), + "mut".to_owned(), + )) + } + _ => None, + }; + + if let Some((span, suggestion)) = sugg { + return Some(AmpMutSugg::Expr { span, suggestion }); } } - let (binding_exists, span) = match opt_ty_info { - // if this is a variable binding with an explicit type, - // then we will suggest changing it to be mutable. - // this is `Applicability::MachineApplicable`. - Some(ty_span) => (true, ty_span), - - // otherwise, we'll suggest *adding* an annotated type, we'll suggest - // the RHS's type for that. - // this is `Applicability::HasPlaceholders`. - None => (false, decl_span), - }; - - // if the binding already exists and is a reference with an explicit - // lifetime, then we can suggest adding ` mut`. this is special-cased from - // the path without an explicit lifetime. - if let Ok(src) = tcx.sess.source_map().span_to_snippet(span) - && src.starts_with("&'") - // note that `& 'a T` is invalid so this is correct. - && let Some(ws_pos) = src.find(char::is_whitespace) - { - let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo(); - Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None }) - // if there is already a binding, we modify it to be `mut` - } else if binding_exists { - // shrink the span to just after the `&` in `&variable` - let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo(); - Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None }) - } else { - // otherwise, suggest that the user annotates the binding; we provide the - // type of the local. - let ty = decl_ty.builtin_deref(true).unwrap(); - - Some(AmpMutSugg { - has_sugg: false, - span, - suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty), - additional: None, - }) - } + Some(AmpMutSugg::ChangeBinding) } /// If the type is a `Coroutine`, `Closure`, or `CoroutineClosure` diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs index 83fe4a9f98f4..f77f759035be 100644 --- a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs +++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs @@ -13,6 +13,8 @@ use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; +use rustc_span::Span; +use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic; use rustc_trait_selection::errors::impl_trait_overcapture_suggestion; use crate::MirBorrowckCtxt; @@ -26,13 +28,61 @@ pub(crate) fn report_opaque_type_errors(&mut self, errors: Vec, ty::OpaqueTypeKey<'tcx>)> = + None; for error in errors { guar = Some(match error { - DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(self.infcx), + DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx), DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => { - self.infcx.dcx().emit_err(err) + infcx.dcx().emit_err(err) } + DeferredOpaqueTypeError::UnexpectedHiddenRegion { + opaque_type_key, + hidden_type, + member_region, + } => { + let named_ty = + self.regioncx.name_regions_for_member_constraint(infcx.tcx, hidden_type.ty); + let named_key = self + .regioncx + .name_regions_for_member_constraint(infcx.tcx, opaque_type_key); + let named_region = + self.regioncx.name_regions_for_member_constraint(infcx.tcx, member_region); + let diag = unexpected_hidden_region_diagnostic( + infcx, + self.mir_def_id(), + hidden_type.span, + named_ty, + named_region, + named_key, + ); + if last_unexpected_hidden_region + != Some((hidden_type.span, named_ty, named_key)) + { + last_unexpected_hidden_region = + Some((hidden_type.span, named_ty, named_key)); + diag.emit() + } else { + diag.delay_as_bug() + } + } + DeferredOpaqueTypeError::NonDefiningUseInDefiningScope { + span, + opaque_type_key, + } => infcx.dcx().span_err( + span, + format!( + "non-defining use of `{}` in the defining scope", + Ty::new_opaque( + infcx.tcx, + opaque_type_key.def_id.to_def_id(), + opaque_type_key.args + ) + ), + ), }); } let guar = guar.unwrap(); @@ -194,7 +244,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { // Find a path between the borrow region and our opaque capture. if let Some((path, _)) = - self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| { + self.regioncx.find_constraint_path_between_regions(self.borrow_region, |r| { r == opaque_region_vid }) { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 2b74f1a48f73..c7d2267e5f70 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -23,7 +23,6 @@ self, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, }; -use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use tracing::{debug, instrument, trace}; @@ -105,18 +104,6 @@ pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). TypeTestError { type_test: TypeTest<'tcx> }, - /// An unexpected hidden region for an opaque type. - UnexpectedHiddenRegion { - /// The span for the member constraint. - span: Span, - /// The hidden type. - hidden_ty: Ty<'tcx>, - /// The opaque type. - key: ty::OpaqueTypeKey<'tcx>, - /// The unexpected region. - member_region: ty::Region<'tcx>, - }, - /// Higher-ranked subtyping error. BoundUniversalRegionError { /// The placeholder free region. @@ -215,7 +202,6 @@ fn suggest_static_lifetime_for_gat_from_hrtb( diag: &mut Diag<'_>, lower_bound: RegionVid, ) { - let mut suggestions = vec![]; let tcx = self.infcx.tcx; // find generic associated types in the given region 'lower_bound' @@ -237,9 +223,11 @@ fn suggest_static_lifetime_for_gat_from_hrtb( .collect::>(); debug!(?gat_id_and_generics); - // find higher-ranked trait bounds bounded to the generic associated types + // Look for the where-bound which introduces the placeholder. + // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>` + // and `T: for<'a> Trait`<'a>. let mut hrtb_bounds = vec![]; - gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| { + gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| { for pred in generics.predicates { let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = pred.kind @@ -248,17 +236,32 @@ fn suggest_static_lifetime_for_gat_from_hrtb( }; if bound_generic_params .iter() - .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id) + .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) .is_some() { for bound in *bounds { hrtb_bounds.push(bound); } + } else { + for bound in *bounds { + if let Trait(trait_bound) = bound { + if trait_bound + .bound_generic_params + .iter() + .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) + .is_some() + { + hrtb_bounds.push(bound); + return; + } + } + } } } }); debug!(?hrtb_bounds); + let mut suggestions = vec![]; hrtb_bounds.iter().for_each(|bound| { let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else { return; @@ -307,11 +310,7 @@ fn suggest_static_lifetime_for_gat_from_hrtb( pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) { // Iterate through all the errors, producing a diagnostic for each one. The diagnostics are // buffered in the `MirBorrowckCtxt`. - let mut outlives_suggestion = OutlivesSuggestionBuilder::default(); - let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> = - None; - for (nll_error, _) in nll_errors.into_iter() { match nll_error { RegionErrorKind::TypeTestError { type_test } => { @@ -362,30 +361,6 @@ pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) { } } - RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => { - let named_ty = - self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, hidden_ty); - let named_key = - self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, key); - let named_region = self - .regioncx - .name_regions_for_member_constraint(self.infcx.tcx, member_region); - let diag = unexpected_hidden_region_diagnostic( - self.infcx, - self.mir_def_id(), - span, - named_ty, - named_region, - named_key, - ); - if last_unexpected_hidden_region != Some((span, named_ty, named_key)) { - self.buffer_error(diag); - last_unexpected_hidden_region = Some((span, named_ty, named_key)); - } else { - diag.delay_as_bug(); - } - } - RegionErrorKind::BoundUniversalRegionError { longer_fr, placeholder, diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 1614c112ab51..4661906fbeb5 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -15,7 +15,6 @@ use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet}; use crate::consumers::OutlivesConstraint; use crate::diagnostics::UniverseInfo; -use crate::member_constraints::MemberConstraintSet; use crate::region_infer::values::{LivenessValues, PlaceholderIndices}; use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest}; use crate::ty::VarianceDiagInfo; @@ -30,7 +29,6 @@ pub(crate) struct LoweredConstraints<'tcx> { pub(crate) constraint_sccs: Sccs, pub(crate) definitions: Frozen>>, pub(crate) scc_annotations: IndexVec, - pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, pub(crate) outlives_constraints: Frozen>, pub(crate) type_tests: Vec>, pub(crate) liveness_constraints: LivenessValues, @@ -147,9 +145,10 @@ fn merge_reached(mut self, other: Self) -> Self { /// Determines if the region variable definitions contain /// placeholders, and compute them for later use. -fn region_definitions<'tcx>( - universal_regions: &UniversalRegions<'tcx>, +// FIXME: This is also used by opaque type handling. Move it to a separate file. +pub(super) fn region_definitions<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, + universal_regions: &UniversalRegions<'tcx>, ) -> (Frozen>>, bool) { let var_infos = infcx.get_region_var_infos(); // Create a RegionDefinition for each inference variable. This happens here because @@ -213,14 +212,13 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, ) -> LoweredConstraints<'tcx> { let universal_regions = &universal_region_relations.universal_regions; - let (definitions, has_placeholders) = region_definitions(universal_regions, infcx); + let (definitions, has_placeholders) = region_definitions(infcx, universal_regions); let MirTypeckRegionConstraints { placeholder_indices, placeholder_index_to_region: _, liveness_constraints, mut outlives_constraints, - member_constraints, universe_causes, type_tests, } = constraints; @@ -246,7 +244,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( return LoweredConstraints { type_tests, - member_constraints, constraint_sccs, scc_annotations: scc_annotations.scc_to_annotation, definitions, @@ -283,7 +280,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( constraint_sccs, definitions, scc_annotations, - member_constraints, outlives_constraints: Frozen::freeze(outlives_constraints), type_tests, liveness_constraints, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index d76d6a04e6e0..ce78ae203a4d 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -78,7 +78,6 @@ mod def_use; mod diagnostics; mod handle_placeholders; -mod member_constraints; mod nll; mod path_utils; mod place_ext; @@ -301,7 +300,7 @@ fn do_mir_borrowck<'tcx>( def: LocalDefId, ) -> PropagatedBorrowCheckResults<'tcx> { let tcx = root_cx.tcx; - let infcx = BorrowckInferCtxt::new(tcx, def); + let infcx = BorrowckInferCtxt::new(tcx, def, root_cx.root_def_id()); let (input_body, promoted) = tcx.mir_promoted(def); let input_body: &Body<'_> = &input_body.borrow(); let input_promoted: &IndexSlice<_, _> = &promoted.borrow(); @@ -335,9 +334,10 @@ fn do_mir_borrowck<'tcx>( // Run the MIR type-checker. let MirTypeckResults { - constraints, + mut constraints, universal_region_relations, - opaque_type_values, + region_bound_pairs, + known_type_outlives_obligations, polonius_context, } = type_check::type_check( root_cx, @@ -352,6 +352,17 @@ fn do_mir_borrowck<'tcx>( Rc::clone(&location_map), ); + let opaque_type_errors = region_infer::opaque_types::handle_opaque_type_uses( + root_cx, + &infcx, + &body, + &universal_region_relations, + ®ion_bound_pairs, + &known_type_outlives_obligations, + &location_map, + &mut constraints, + ); + // Compute non-lexical lifetimes using the constraints computed // by typechecking the MIR body. let nll::NllOutput { @@ -375,8 +386,6 @@ fn do_mir_borrowck<'tcx>( polonius_context, ); - let opaque_type_errors = regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values); - // Dump MIR results into a file, if that is enabled. This lets us // write unit-tests, as well as helping with debugging. nll::dump_nll_mir(&infcx, body, ®ioncx, &opt_closure_req, &borrow_set); @@ -581,12 +590,13 @@ fn get_flow_results<'a, 'tcx>( pub(crate) struct BorrowckInferCtxt<'tcx> { pub(crate) infcx: InferCtxt<'tcx>, - pub(crate) reg_var_to_origin: RefCell>, + pub(crate) root_def_id: LocalDefId, pub(crate) param_env: ParamEnv<'tcx>, + pub(crate) reg_var_to_origin: RefCell>, } impl<'tcx> BorrowckInferCtxt<'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId, root_def_id: LocalDefId) -> Self { let typing_mode = if tcx.use_typing_mode_borrowck() { TypingMode::borrowck(tcx, def_id) } else { @@ -594,7 +604,12 @@ pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { }; let infcx = tcx.infer_ctxt().build(typing_mode); let param_env = tcx.param_env(def_id); - BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env } + BorrowckInferCtxt { + infcx, + root_def_id, + reg_var_to_origin: RefCell::new(Default::default()), + param_env, + } } pub(crate) fn next_region_var( diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs deleted file mode 100644 index bdd0f6fe11e0..000000000000 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ /dev/null @@ -1,226 +0,0 @@ -use std::hash::Hash; -use std::ops::Index; - -use rustc_data_structures::fx::FxIndexMap; -use rustc_index::{IndexSlice, IndexVec}; -use rustc_middle::ty::{self, Ty}; -use rustc_span::Span; -use tracing::instrument; - -/// Compactly stores a set of `R0 member of [R1...Rn]` constraints, -/// indexed by the region `R0`. -#[derive(Debug)] -pub(crate) struct MemberConstraintSet<'tcx, R> -where - R: Copy + Eq, -{ - /// Stores the first "member" constraint for a given `R0`. This is an - /// index into the `constraints` vector below. - first_constraints: FxIndexMap, - - /// Stores the data about each `R0 member of [R1..Rn]` constraint. - /// These are organized into a linked list, so each constraint - /// contains the index of the next constraint with the same `R0`. - constraints: IndexVec>, - - /// Stores the `R1..Rn` regions for *all* sets. For any given - /// constraint, we keep two indices so that we can pull out a - /// slice. - choice_regions: Vec, -} - -/// Represents a `R0 member of [R1..Rn]` constraint -#[derive(Debug)] -pub(crate) struct MemberConstraint<'tcx> { - next_constraint: Option, - - /// The span where the hidden type was instantiated. - pub(crate) definition_span: Span, - - /// The hidden type in which `R0` appears. (Used in error reporting.) - pub(crate) hidden_ty: Ty<'tcx>, - - pub(crate) key: ty::OpaqueTypeKey<'tcx>, - - /// The region `R0`. - pub(crate) member_region_vid: ty::RegionVid, - - /// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`. - start_index: usize, - - /// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`. - end_index: usize, -} - -rustc_index::newtype_index! { - #[debug_format = "MemberConstraintIndex({})"] - pub(crate) struct NllMemberConstraintIndex {} -} - -impl Default for MemberConstraintSet<'_, ty::RegionVid> { - fn default() -> Self { - Self { - first_constraints: Default::default(), - constraints: Default::default(), - choice_regions: Default::default(), - } - } -} - -impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { - pub(crate) fn is_empty(&self) -> bool { - self.constraints.is_empty() - } - - /// Pushes a member constraint into the set. - #[instrument(level = "debug", skip(self))] - pub(crate) fn add_member_constraint( - &mut self, - key: ty::OpaqueTypeKey<'tcx>, - hidden_ty: Ty<'tcx>, - definition_span: Span, - member_region_vid: ty::RegionVid, - choice_regions: &[ty::RegionVid], - ) { - let next_constraint = self.first_constraints.get(&member_region_vid).cloned(); - let start_index = self.choice_regions.len(); - self.choice_regions.extend(choice_regions); - let end_index = self.choice_regions.len(); - let constraint_index = self.constraints.push(MemberConstraint { - next_constraint, - member_region_vid, - definition_span, - hidden_ty, - key, - start_index, - end_index, - }); - self.first_constraints.insert(member_region_vid, constraint_index); - } -} - -impl<'tcx, R1> MemberConstraintSet<'tcx, R1> -where - R1: Copy + Hash + Eq, -{ - /// Remap the "member region" key using `map_fn`, producing a new - /// member constraint set. This is used in the NLL code to map from - /// the original `RegionVid` to an scc index. In some cases, we - /// may have multiple `R1` values mapping to the same `R2` key -- that - /// is ok, the two sets will be merged. - pub(crate) fn into_mapped( - self, - mut map_fn: impl FnMut(R1) -> R2, - ) -> MemberConstraintSet<'tcx, R2> - where - R2: Copy + Hash + Eq, - { - // We can re-use most of the original data, just tweaking the - // linked list links a bit. - // - // For example if we had two keys `Ra` and `Rb` that both now - // wind up mapped to the same key `S`, we would append the - // linked list for `Ra` onto the end of the linked list for - // `Rb` (or vice versa) -- this basically just requires - // rewriting the final link from one list to point at the other - // other (see `append_list`). - - let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self; - - let mut first_constraints2 = FxIndexMap::default(); - first_constraints2.reserve(first_constraints.len()); - - for (r1, start1) in first_constraints { - let r2 = map_fn(r1); - if let Some(&start2) = first_constraints2.get(&r2) { - append_list(&mut constraints, start1, start2); - } - first_constraints2.insert(r2, start1); - } - - MemberConstraintSet { first_constraints: first_constraints2, constraints, choice_regions } - } -} - -impl<'tcx, R> MemberConstraintSet<'tcx, R> -where - R: Copy + Hash + Eq, -{ - pub(crate) fn all_indices(&self) -> impl Iterator { - self.constraints.indices() - } - - /// Iterate down the constraint indices associated with a given - /// peek-region. You can then use `choice_regions` and other - /// methods to access data. - pub(crate) fn indices( - &self, - member_region_vid: R, - ) -> impl Iterator { - let mut next = self.first_constraints.get(&member_region_vid).cloned(); - std::iter::from_fn(move || -> Option { - if let Some(current) = next { - next = self.constraints[current].next_constraint; - Some(current) - } else { - None - } - }) - } - - /// Returns the "choice regions" for a given member - /// constraint. This is the `R1..Rn` from a constraint like: - /// - /// ```text - /// R0 member of [R1..Rn] - /// ``` - pub(crate) fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] { - let MemberConstraint { start_index, end_index, .. } = &self.constraints[pci]; - &self.choice_regions[*start_index..*end_index] - } -} - -impl<'tcx, R> Index for MemberConstraintSet<'tcx, R> -where - R: Copy + Eq, -{ - type Output = MemberConstraint<'tcx>; - - fn index(&self, i: NllMemberConstraintIndex) -> &MemberConstraint<'tcx> { - &self.constraints[i] - } -} - -/// Given a linked list starting at `source_list` and another linked -/// list starting at `target_list`, modify `target_list` so that it is -/// followed by `source_list`. -/// -/// Before: -/// -/// ```text -/// target_list: A -> B -> C -> (None) -/// source_list: D -> E -> F -> (None) -/// ``` -/// -/// After: -/// -/// ```text -/// target_list: A -> B -> C -> D -> E -> F -> (None) -/// ``` -fn append_list( - constraints: &mut IndexSlice>, - target_list: NllMemberConstraintIndex, - source_list: NllMemberConstraintIndex, -) { - let mut p = target_list; - loop { - let r = &mut constraints[p]; - match r.next_constraint { - Some(q) => p = q, - None => { - r.next_constraint = Some(source_list); - return; - } - } - } -} diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index c76c5c17431a..3d95eb4663ac 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1,8 +1,6 @@ -use std::cell::OnceCell; use std::collections::VecDeque; 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::{self, Sccs}; @@ -24,15 +22,13 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::{Level, debug, enabled, instrument, trace}; -use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph}; +use crate::constraints::graph::NormalConstraintGraph; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; -use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex}; use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; -use crate::region_infer::reverse_sccs::ReverseSccGraph; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; use crate::type_check::Locations; use crate::type_check::free_region_relations::UniversalRegionRelations; @@ -120,20 +116,6 @@ pub struct RegionInferenceContext<'tcx> { scc_annotations: IndexVec, - /// 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 - /// to outlive a given SCC. - rev_scc_graph: OnceCell, - - /// The "R0 member of [R1..Rn]" constraints, indexed by SCC. - member_constraints: Rc>, - - /// Records the member constraints that we applied to each scc. - /// This is useful for error reporting. Once constraint - /// propagation is done, this vector is sorted according to - /// `member_region_scc`. - member_constraints_applied: Vec, - /// Map universe indexes to information on why we created it. universe_causes: FxIndexMap>, @@ -150,32 +132,6 @@ pub struct RegionInferenceContext<'tcx> { universal_region_relations: Frozen>, } -/// Each time that `apply_member_constraint` is successful, it appends -/// one of these structs to the `member_constraints_applied` field. -/// This is used in error reporting to trace out what happened. -/// -/// The way that `apply_member_constraint` works is that it effectively -/// adds a new lower bound to the SCC it is analyzing: so you wind up -/// with `'R: 'O` where `'R` is the pick-region and `'O` is the -/// minimal viable option. -#[derive(Debug)] -pub(crate) struct AppliedMemberConstraint { - /// The SCC that was affected. (The "member region".) - /// - /// The vector if `AppliedMemberConstraint` elements is kept sorted - /// by this field. - pub(crate) member_region_scc: ConstraintSccIndex, - - /// The "best option" that `apply_member_constraint` found -- this was - /// added as an "ad-hoc" lower-bound to `member_region_scc`. - pub(crate) min_choice: ty::RegionVid, - - /// The "member constraint index" -- we can find out details about - /// the constraint from - /// `set.member_constraints[member_constraint_index]`. - pub(crate) member_constraint_index: NllMemberConstraintIndex, -} - #[derive(Debug)] pub(crate) struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential @@ -268,7 +224,6 @@ enum Trace<'a, 'tcx> { StartRegion, FromGraph(&'a OutlivesConstraint<'tcx>), FromStatic(RegionVid), - FromMember(RegionVid, RegionVid, Span), NotVisited, } @@ -363,7 +318,6 @@ pub(crate) fn new( liveness_constraints, universe_causes, placeholder_indices, - member_constraints, } = lowered_constraints; debug!("universal_regions: {:#?}", universal_region_relations.universal_regions); @@ -385,9 +339,6 @@ pub(crate) fn new( scc_values.merge_liveness(scc, region, &liveness_constraints); } - let member_constraints = - Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r))); - let mut result = Self { definitions, liveness_constraints, @@ -395,9 +346,6 @@ pub(crate) fn new( constraint_graph, constraint_sccs, scc_annotations, - rev_scc_graph: OnceCell::new(), - member_constraints, - member_constraints_applied: Vec::new(), universe_causes, scc_values, type_tests, @@ -550,19 +498,6 @@ pub(crate) fn placeholders_contained_in( self.scc_values.placeholders_contained_in(scc) } - /// Once region solving has completed, this function will return the member constraints that - /// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`. - pub(crate) fn applied_member_constraints( - &self, - scc: ConstraintSccIndex, - ) -> &[AppliedMemberConstraint] { - binary_search_util::binary_search_slice( - &self.member_constraints_applied, - |applied| applied.member_region_scc, - &scc, - ) - } - /// Performs region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. @@ -607,12 +542,6 @@ pub(super) fn solve( debug!(?errors_buffer); - if errors_buffer.is_empty() { - self.check_member_constraints(infcx, &mut errors_buffer); - } - - debug!(?errors_buffer); - let outlives_requirements = outlives_requirements.unwrap_or_default(); if outlives_requirements.is_empty() { @@ -642,146 +571,15 @@ fn propagate_constraints(&mut self) { }); // To propagate constraints, we walk the DAG induced by the - // SCC. For each SCC, we visit its successors and compute + // SCC. For each SCC `A`, we visit its successors and compute // their values, then we union all those values to get our // own. - for scc in self.constraint_sccs.all_sccs() { - self.compute_value_for_scc(scc); - } - - // Sort the applied member constraints so we can binary search - // through them later. - self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc); - } - - /// Computes the value of the SCC `scc_a`, which has not yet been - /// computed, by unioning the values of its successors. - /// Assumes that all successors have been computed already - /// (which is assured by iterating over SCCs in dependency order). - #[instrument(skip(self), level = "debug")] - fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) { - // Walk each SCC `B` such that `A: B`... - for &scc_b in self.constraint_sccs.successors(scc_a) { - debug!(?scc_b); - self.scc_values.add_region(scc_a, scc_b); - } - - // Now take member constraints into account. - let member_constraints = Rc::clone(&self.member_constraints); - for m_c_i in member_constraints.indices(scc_a) { - self.apply_member_constraint(scc_a, m_c_i, member_constraints.choice_regions(m_c_i)); - } - - debug!(value = ?self.scc_values.region_value_str(scc_a)); - } - - /// Invoked for each `R0 member of [R1..Rn]` constraint. - /// - /// `scc` is the SCC containing R0, and `choice_regions` are the - /// `R1..Rn` regions -- they are always known to be universal - /// regions (and if that's not true, we just don't attempt to - /// enforce the constraint). - /// - /// The current value of `scc` at the time the method is invoked - /// is considered a *lower bound*. If possible, we will modify - /// the constraint to set it equal to one of the option regions. - /// If we make any changes, returns true, else false. - /// - /// This function only adds the member constraints to the region graph, - /// it does not check them. They are later checked in - /// `check_member_constraints` after the region graph has been computed. - #[instrument(skip(self, member_constraint_index), level = "debug")] - fn apply_member_constraint( - &mut self, - scc: ConstraintSccIndex, - member_constraint_index: NllMemberConstraintIndex, - choice_regions: &[ty::RegionVid], - ) { - // Create a mutable vector of the options. We'll try to winnow - // them down. - let mut choice_regions: Vec = choice_regions.to_vec(); - - // Convert to the SCC representative: sometimes we have inference - // variables in the member constraint that wind up equated with - // universal regions. The scc representative is the minimal numbered - // one from the corresponding scc so it will be the universal region - // if one exists. - for c_r in &mut choice_regions { - let scc = self.constraint_sccs.scc(*c_r); - *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.max_placeholder_universe_reached(scc).is_root() { - return; - } - - // The existing value for `scc` is a lower-bound. This will - // consist of some set `{P} + {LB}` of points `{P}` and - // lower-bound free regions `{LB}`. As each choice region `O` - // is a free region, it will outlive the points. But we can - // only consider the option `O` if `O: LB`. - choice_regions.retain(|&o_r| { - self.scc_values - .universal_regions_outlived_by(scc) - .all(|lb| self.universal_region_relations.outlives(o_r, lb)) - }); - debug!(?choice_regions, "after lb"); - - // Now find all the *upper bounds* -- that is, each UB is a - // free region that must outlive the member region `R0` (`UB: - // R0`). Therefore, we need only keep an option `O` if `UB: O` - // for all UB. - let universal_region_relations = &self.universal_region_relations; - for ub in self.reverse_scc_graph().upper_bounds(scc) { - debug!(?ub); - choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r)); - } - debug!(?choice_regions, "after ub"); - - // At this point we can pick any member of `choice_regions` and would like to choose - // it to be a small as possible. To avoid potential non-determinism we will pick the - // smallest such choice. - // - // Because universal regions are only partially ordered (i.e, not every two regions are - // comparable), we will ignore any region that doesn't compare to all others when picking - // the minimum choice. - // - // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where - // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`. - // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`. - let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| { - choice_regions.iter().all(|&r2| { - self.universal_region_relations.outlives(r1, r2) - || self.universal_region_relations.outlives(r2, r1) - }) - }); - // Now we're left with `['static, 'c]`. Pick `'c` as the minimum! - let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| { - let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2); - let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1); - match (r1_outlives_r2, r2_outlives_r1) { - (true, true) => r1.min(r2), - (true, false) => r2, - (false, true) => r1, - (false, false) => bug!("incomparable regions in total order"), + for scc_a in self.constraint_sccs.all_sccs() { + // Walk each SCC `B` such that `A: B`... + for &scc_b in self.constraint_sccs.successors(scc_a) { + debug!(?scc_b); + self.scc_values.add_region(scc_a, scc_b); } - }) else { - debug!("no unique minimum choice"); - return; - }; - - // As we require `'scc: 'min_choice`, we have definitely already computed - // its `scc_values` at this point. - let min_choice_scc = self.constraint_sccs.scc(min_choice); - debug!(?min_choice, ?min_choice_scc); - if self.scc_values.add_region(scc, min_choice_scc) { - self.member_constraints_applied.push(AppliedMemberConstraint { - member_region_scc: scc, - min_choice, - member_constraint_index, - }); } } @@ -1376,13 +1174,6 @@ fn max_nameable_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { self.scc_annotations[scc].max_nameable_universe() } - pub(crate) fn max_placeholder_universe_reached( - &self, - scc: ConstraintSccIndex, - ) -> UniverseIndex { - self.scc_annotations[scc].max_placeholder_universe_reached() - } - /// 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 != @@ -1551,43 +1342,6 @@ fn check_bound_universal_region( } } - #[instrument(level = "debug", skip(self, infcx, errors_buffer))] - fn check_member_constraints( - &self, - infcx: &InferCtxt<'tcx>, - errors_buffer: &mut RegionErrors<'tcx>, - ) { - let member_constraints = Rc::clone(&self.member_constraints); - for m_c_i in member_constraints.all_indices() { - debug!(?m_c_i); - let m_c = &member_constraints[m_c_i]; - let member_region_vid = m_c.member_region_vid; - debug!( - ?member_region_vid, - value = ?self.region_value_str(member_region_vid), - ); - let choice_regions = member_constraints.choice_regions(m_c_i); - debug!(?choice_regions); - - // Did the member region wind up equal to any of the option regions? - if let Some(o) = - choice_regions.iter().find(|&&o_r| self.eval_equal(o_r, m_c.member_region_vid)) - { - debug!("evaluated as equal to {:?}", o); - continue; - } - - // If not, report an error. - let member_region = ty::Region::new_var(infcx.tcx, member_region_vid); - errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion { - span: m_c.definition_span, - hidden_ty: m_c.hidden_ty, - key: m_c.key, - member_region, - }); - } - } - /// We have a constraint `fr1: fr2` that is not satisfied, where /// `fr2` represents some universal region. Here, `r` is some /// region where we know that `fr1: r` and this function has the @@ -1651,18 +1405,39 @@ pub(crate) fn find_outlives_blame_span( } /// Walks the graph of constraints (where `'a: 'b` is considered - /// an edge `'a -> 'b`) to find all paths from `from_region` to - /// `to_region`. The paths are accumulated into the vector - /// `results`. The paths are stored as a series of - /// `ConstraintIndex` values -- in other words, a list of *edges*. + /// an edge `'a -> 'b`) to find a path from `from_region` to + /// `to_region`. /// /// 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( + pub(crate) fn find_constraint_path_between_regions( &self, from_region: RegionVid, target_test: impl Fn(RegionVid) -> bool, + ) -> Option<(Vec>, RegionVid)> { + self.find_constraint_path_between_regions_inner(true, from_region, &target_test).or_else( + || self.find_constraint_path_between_regions_inner(false, from_region, &target_test), + ) + } + + /// The constraints we get from equating the hidden type of each use of an opaque + /// with its final concrete type may end up getting preferred over other, potentially + /// longer constraint paths. + /// + /// Given that we compute the final concrete type by relying on this existing constraint + /// path, this can easily end up hiding the actual reason for why we require these regions + /// to be equal. + /// + /// To handle this, we first look at the path while ignoring these constraints and then + /// retry while considering them. This is not perfect, as the `from_region` may have already + /// been partially related to its argument region, so while we rely on a member constraint + /// to get a complete path, the most relevant step of that path already existed before then. + fn find_constraint_path_between_regions_inner( + &self, + ignore_opaque_type_constraints: bool, + from_region: RegionVid, + target_test: impl Fn(RegionVid) -> bool, ) -> Option<(Vec>, RegionVid)> { let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions); context[from_region] = Trace::StartRegion; @@ -1677,7 +1452,7 @@ pub(crate) fn find_constraint_paths_between_regions( while let Some(r) = deque.pop_front() { debug!( - "find_constraint_paths_between_regions: from_region={:?} r={:?} value={}", + "find_constraint_path_between_regions: from_region={:?} r={:?} value={}", from_region, r, self.region_value_str(r), @@ -1711,20 +1486,6 @@ pub(crate) fn find_constraint_paths_between_regions( result.push(c); } - Trace::FromMember(sup, sub, span) => { - let c = OutlivesConstraint { - sup, - sub, - locations: Locations::All(span), - span, - category: ConstraintCategory::OpaqueType, - variance_info: ty::VarianceDiagInfo::default(), - from_closure: false, - }; - p = c.sup; - result.push(c); - } - Trace::StartRegion => { result.reverse(); return Some((result, r)); @@ -1763,23 +1524,21 @@ pub(crate) fn find_constraint_paths_between_regions( let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints); // This loop can be hot. for constraint in edges { - if matches!(constraint.category, ConstraintCategory::IllegalUniverse) { - debug!("Ignoring illegal universe constraint: {constraint:?}"); - continue; + match constraint.category { + ConstraintCategory::IllegalUniverse => { + debug!("Ignoring illegal universe constraint: {constraint:?}"); + continue; + } + ConstraintCategory::OpaqueType if ignore_opaque_type_constraints => { + debug!("Ignoring member constraint: {constraint:?}"); + continue; + } + _ => {} } debug_assert_eq!(constraint.sup, r); handle_trace(constraint.sub, Trace::FromGraph(constraint)); } } - - // Member constraints can also give rise to `'r: 'x` edges that - // were not part of the graph initially, so watch out for those. - // (But they are extremely rare; this loop is very cold.) - for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) { - let sub = constraint.min_choice; - let p_c = &self.member_constraints[constraint.member_constraint_index]; - handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span)); - } } None @@ -1790,7 +1549,7 @@ pub(crate) fn find_constraint_paths_between_regions( pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid { trace!(scc = ?self.constraint_sccs.scc(fr1)); trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1))); - self.find_constraint_paths_between_regions(fr1, |r| { + self.find_constraint_path_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)); self.liveness_constraints.is_live_at(r, location) @@ -1800,9 +1559,7 @@ pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) // `fr1: r` and `r` is a placeholder from some universe // `fr1` cannot name. This would force `fr1` to be // `'static`. - self.find_constraint_paths_between_regions(fr1, |r| { - self.cannot_name_placeholder(fr1, r) - }) + self.find_constraint_path_between_regions(fr1, |r| self.cannot_name_placeholder(fr1, r)) }) .or_else(|| { // If we fail to find THAT, it may be that `fr1` is a @@ -1815,9 +1572,7 @@ pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) // must be able to name the universe of R2, because R2 will // be at least `'empty(Universe(R2))`, and `R1` must be at // larger than that. - self.find_constraint_paths_between_regions(fr1, |r| { - self.cannot_name_placeholder(r, fr1) - }) + self.find_constraint_path_between_regions(fr1, |r| self.cannot_name_placeholder(r, fr1)) }) .map(|(_path, r)| r) .unwrap() @@ -1873,9 +1628,9 @@ pub(crate) fn best_blame_constraint( ) -> (BlameConstraint<'tcx>, Vec>) { // Find all paths let (path, target_region) = self - .find_constraint_paths_between_regions(from_region, target_test) + .find_constraint_path_between_regions(from_region, target_test) .or_else(|| { - self.find_constraint_paths_between_regions(from_region, |r| { + self.find_constraint_path_between_regions(from_region, |r| { self.cannot_name_placeholder(from_region, r) }) }) @@ -2111,11 +1866,6 @@ pub fn constraint_sccs(&self) -> &ConstraintSccs { &self.constraint_sccs } - /// Access to the region graph, built from the outlives constraints. - pub(crate) fn region_graph(&self) -> RegionGraph<'_, 'tcx, graph::Normal> { - self.constraint_graph.region_graph(&self.constraints, self.universal_regions().fr_static) - } - /// Returns the representative `RegionVid` for a given SCC. /// See `RegionTracker` for how a region variable ID is chosen. /// diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs deleted file mode 100644 index 23c4554aa155..000000000000 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ /dev/null @@ -1,299 +0,0 @@ -use rustc_data_structures::fx::FxIndexMap; -use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; -use rustc_macros::extension; -use rustc_middle::ty::{ - self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, fold_regions, -}; -use rustc_span::Span; -use rustc_trait_selection::opaque_types::{ - InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid, -}; -use tracing::{debug, instrument}; - -use super::RegionInferenceContext; -use crate::BorrowCheckRootCtxt; -use crate::session_diagnostics::LifetimeMismatchOpaqueParam; -use crate::universal_regions::RegionClassification; - -pub(crate) enum DeferredOpaqueTypeError<'tcx> { - InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>), - LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>), -} - -impl<'tcx> RegionInferenceContext<'tcx> { - /// Resolve any opaque types that were encountered while borrow checking - /// this item. This is then used to get the type in the `type_of` query. - /// - /// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`. - /// This is lowered to give HIR something like - /// - /// type f<'a>::_Return<'_x> = impl Sized + '_x; - /// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x } - /// - /// When checking the return type record the type from the return and the - /// type used in the return value. In this case they might be `_Return<'1>` - /// and `&'2 i32` respectively. - /// - /// Once we to this method, we have completed region inference and want to - /// call `infer_opaque_definition_from_instantiation` to get the inferred - /// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation` - /// compares lifetimes directly, so we need to map the inference variables - /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`. - /// - /// First we map the regions in the generic parameters `_Return<'1>` to - /// their `external_name` giving `_Return<'a>`. This step is a bit involved. - /// See the [rustc-dev-guide chapter] for more info. - /// - /// Then we map all the lifetimes in the concrete type to an equal - /// universal region that occurs in the opaque type's args, in this case - /// this would result in `&'a i32`. We only consider regions in the args - /// in case there is an equal region that does not. For example, this should - /// be allowed: - /// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }` - /// - /// This will then allow `infer_opaque_definition_from_instantiation` to - /// determine that `_Return<'_x> = &'_x i32`. - /// - /// There's a slight complication around closures. Given - /// `fn f<'a: 'a>() { || {} }` the closure's type is something like - /// `f::<'a>::{{closure}}`. The region parameter from f is essentially - /// ignored by type checking so ends up being inferred to an empty region. - /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`, - /// which has no `external_name` in which case we use `'{erased}` as the - /// region to pass to `infer_opaque_definition_from_instantiation`. - /// - /// [rustc-dev-guide chapter]: - /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html - #[instrument(level = "debug", skip(self, root_cx, infcx))] - pub(crate) fn infer_opaque_types( - &self, - root_cx: &mut BorrowCheckRootCtxt<'tcx>, - infcx: &InferCtxt<'tcx>, - opaque_ty_decls: FxIndexMap, OpaqueHiddenType<'tcx>>, - ) -> Vec> { - let mut errors = Vec::new(); - let mut decls_modulo_regions: FxIndexMap, (OpaqueTypeKey<'tcx>, Span)> = - FxIndexMap::default(); - - for (opaque_type_key, concrete_type) in opaque_ty_decls { - debug!(?opaque_type_key, ?concrete_type); - - let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> = - vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)]; - - let opaque_type_key = - opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| { - // 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_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 - // the same name and simplifies subsequent handling. - // See [rustc-dev-guide chapter] § "Semantic lifetime equality". - NllRegionVariableOrigin::FreeRegion => self - .universal_regions() - .universal_regions_iter() - .filter(|&ur| { - // See [rustc-dev-guide chapter] § "Closure restrictions". - !matches!( - self.universal_regions().region_classification(ur), - Some(RegionClassification::External) - ) - }) - .find(|&ur| self.universal_region_relations.equal(vid, ur)) - .map(|ur| self.definitions[ur].external_name.unwrap()), - NllRegionVariableOrigin::Placeholder(placeholder) => { - Some(ty::Region::new_placeholder(infcx.tcx, placeholder)) - } - NllRegionVariableOrigin::Existential { .. } => None, - } - .unwrap_or_else(|| { - ty::Region::new_error_with_message( - infcx.tcx, - concrete_type.span, - "opaque type with non-universal region args", - ) - }); - - arg_regions.push((vid, named)); - named - }); - debug!(?opaque_type_key, ?arg_regions); - - let concrete_type = fold_regions(infcx.tcx, concrete_type, |region, _| { - arg_regions - .iter() - .find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid)) - .map(|&(_, arg_named)| arg_named) - .unwrap_or(infcx.tcx.lifetimes.re_erased) - }); - debug!(?concrete_type); - - let ty = match infcx - .infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type) - { - Ok(ty) => ty, - Err(err) => { - errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err)); - continue; - } - }; - - // Sometimes, when the hidden type is an inference variable, it can happen that - // the hidden type becomes the opaque type itself. In this case, this was an opaque - // usage of the opaque type and we can ignore it. This check is mirrored in typeck's - // writeback. - if !infcx.next_trait_solver() { - if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() - && alias_ty.def_id == opaque_type_key.def_id.to_def_id() - && alias_ty.args == opaque_type_key.args - { - continue; - } - } - - root_cx.add_concrete_opaque_type( - opaque_type_key.def_id, - OpaqueHiddenType { span: concrete_type.span, ty }, - ); - - // Check that all opaque types have the same region parameters if they have the same - // non-region parameters. This is necessary because within the new solver we perform - // various query operations modulo regions, and thus could unsoundly select some impls - // that don't hold. - if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert( - infcx.tcx.erase_regions(opaque_type_key), - (opaque_type_key, concrete_type.span), - ) && let Some((arg1, arg2)) = std::iter::zip( - prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), - opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), - ) - .find(|(arg1, arg2)| arg1 != arg2) - { - errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam( - LifetimeMismatchOpaqueParam { - arg: arg1, - prev: arg2, - span: prev_span, - prev_span: concrete_type.span, - }, - )); - } - } - - errors - } - - /// Map the regions in the type to named regions. This is similar to what - /// `infer_opaque_types` does, but can infer any universal region, not only - /// ones from the args for the opaque type. It also doesn't double check - /// that the regions produced are in fact equal to the named region they are - /// replaced with. This is fine because this function is only to improve the - /// region names in error messages. - /// - /// This differs from `MirBorrowckCtxt::name_regions` since it is particularly - /// lax with mapping region vids that are *shorter* than a universal region to - /// that universal region. This is useful for member region constraints since - /// we want to suggest a universal region name to capture even if it's technically - /// not equal to the error region. - pub(crate) fn name_regions_for_member_constraint(&self, tcx: TyCtxt<'tcx>, ty: T) -> T - where - T: TypeFoldable>, - { - fold_regions(tcx, ty, |region, _| match region.kind() { - ty::ReVar(vid) => { - let scc = self.constraint_sccs.scc(vid); - - // Special handling of higher-ranked regions. - if !self.max_nameable_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)) => { - return ty::Region::new_placeholder(tcx, placeholder); - } - - // Fallback: this will produce a cryptic error message. - _ => return region, - } - } - - // Find something that we can name - let upper_bound = self.approx_universal_upper_bound(vid); - if let Some(universal_region) = self.definitions[upper_bound].external_name { - return universal_region; - } - - // Nothing exact found, so we pick a named upper bound, if there's only one. - // If there's >1 universal region, then we probably are dealing w/ an intersection - // region which cannot be mapped back to a universal. - // FIXME: We could probably compute the LUB if there is one. - let scc = self.constraint_sccs.scc(vid); - let upper_bounds: Vec<_> = self - .reverse_scc_graph() - .upper_bounds(scc) - .filter_map(|vid| self.definitions[vid].external_name) - .filter(|r| !r.is_static()) - .collect(); - match &upper_bounds[..] { - [universal_region] => *universal_region, - _ => region, - } - } - _ => region, - }) - } -} - -#[extension(pub trait InferCtxtExt<'tcx>)] -impl<'tcx> InferCtxt<'tcx> { - /// Given the fully resolved, instantiated type for an opaque - /// type, i.e., the value of an inference variable like C1 or C2 - /// (*), computes the "definition type" for an opaque type - /// definition -- that is, the inferred value of `Foo1<'x>` or - /// `Foo2<'x>` that we would conceptually use in its definition: - /// ```ignore (illustrative) - /// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA - /// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB - /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } - /// ``` - /// Note that these values are defined in terms of a distinct set of - /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main - /// purpose of this function is to do that translation. - /// - /// (*) C1 and C2 were introduced in the comments on - /// `register_member_constraints`. Read that comment for more context. - /// - /// # Parameters - /// - /// - `def_id`, the `impl Trait` type - /// - `args`, the args used to instantiate this opaque type - /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of - /// `opaque_defn.concrete_ty` - #[instrument(level = "debug", skip(self))] - fn infer_opaque_definition_from_instantiation( - &self, - opaque_type_key: OpaqueTypeKey<'tcx>, - instantiated_ty: OpaqueHiddenType<'tcx>, - ) -> Result, InvalidOpaqueTypeArgs<'tcx>> { - check_opaque_type_parameter_valid( - self, - opaque_type_key, - instantiated_ty.span, - DefiningScopeKind::MirBorrowck, - )?; - - let definition_ty = instantiated_ty - .remap_generic_params_to_declaration_params( - opaque_type_key, - self.tcx, - DefiningScopeKind::MirBorrowck, - ) - .ty; - - definition_ty.error_reported()?; - Ok(definition_ty) - } -} diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs new file mode 100644 index 000000000000..667fc440ac00 --- /dev/null +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs @@ -0,0 +1,194 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; +use rustc_middle::bug; +use rustc_middle::ty::{ + self, GenericArgsRef, Region, RegionVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitor, +}; +use tracing::{debug, instrument}; + +use super::DefiningUse; +use super::region_ctxt::RegionCtxt; +use crate::constraints::ConstraintSccIndex; + +pub(super) fn apply_member_constraints<'tcx>( + rcx: &mut RegionCtxt<'_, 'tcx>, + defining_uses: &[DefiningUse<'tcx>], +) { + // Start by collecting the member constraints of all defining uses. + // + // Applying member constraints can influence other member constraints, + // so we first collect and then apply them. + let mut member_constraints = Default::default(); + for defining_use in defining_uses { + let mut visitor = CollectMemberConstraintsVisitor { + rcx, + defining_use, + member_constraints: &mut member_constraints, + }; + defining_use.hidden_type.ty.visit_with(&mut visitor); + } + + // Now walk over the region graph, visiting the smallest regions first and then all + // regions which have to outlive that one. + // + // Whenever we encounter a member region, we mutate the value of this SCC. This is + // as if we'd introduce new outlives constraints. However, we discard these region + // values after we've inferred the hidden types of opaques and apply the region + // constraints by simply equating the actual hidden type with the inferred one. + debug!(?member_constraints); + for scc_a in rcx.constraint_sccs.all_sccs() { + debug!(?scc_a); + // Start by adding the region values required by outlives constraints. This + // matches how we compute the final region values in `fn compute_regions`. + // + // We need to do this here to get a lower bound when applying member constraints. + // This propagates the region values added by previous member constraints. + for &scc_b in rcx.constraint_sccs.successors(scc_a) { + debug!(?scc_b); + rcx.scc_values.add_region(scc_a, scc_b); + } + + for defining_use in member_constraints.get(&scc_a).into_iter().flatten() { + apply_member_constraint(rcx, scc_a, &defining_use.arg_regions); + } + } +} + +#[instrument(level = "debug", skip(rcx))] +fn apply_member_constraint<'tcx>( + rcx: &mut RegionCtxt<'_, 'tcx>, + member: ConstraintSccIndex, + arg_regions: &[RegionVid], +) { + // If the member region lives in a higher universe, we currently choose + // the most conservative option by leaving it unchanged. + if !rcx.max_placeholder_universe_reached(member).is_root() { + return; + } + + // The existing value of `'member` is a lower-bound. If its is already larger than + // some universal region, we cannot equate it with that region. Said differently, we + // ignore choice regions which are smaller than this member region. + let mut choice_regions = arg_regions + .iter() + .copied() + .map(|r| rcx.representative(r).rvid()) + .filter(|&choice_region| { + rcx.scc_values.universal_regions_outlived_by(member).all(|lower_bound| { + rcx.universal_region_relations.outlives(choice_region, lower_bound) + }) + }) + .collect::>(); + debug!(?choice_regions, "after enforcing lower-bound"); + + // Now find all the *upper bounds* -- that is, each UB is a + // free region that must outlive the member region `R0` (`UB: + // R0`). Therefore, we need only keep an option `O` if `UB: O` + // for all UB. + // + // If we have a requirement `'upper_bound: 'member`, equating `'member` + // with some region `'choice` means we now also require `'upper_bound: 'choice`. + // Avoid choice regions for which this does not hold. + for ub in rcx.rev_scc_graph.upper_bounds(member) { + choice_regions + .retain(|&choice_region| rcx.universal_region_relations.outlives(ub, choice_region)); + } + debug!(?choice_regions, "after enforcing upper-bound"); + + // At this point we can pick any member of `choice_regions` and would like to choose + // it to be a small as possible. To avoid potential non-determinism we will pick the + // smallest such choice. + // + // Because universal regions are only partially ordered (i.e, not every two regions are + // comparable), we will ignore any region that doesn't compare to all others when picking + // the minimum choice. + // + // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where + // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`. + // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`. + let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| { + choice_regions.iter().all(|&r2| { + rcx.universal_region_relations.outlives(r1, r2) + || rcx.universal_region_relations.outlives(r2, r1) + }) + }); + // Now we're left with `['static, 'c]`. Pick `'c` as the minimum! + let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| { + let r1_outlives_r2 = rcx.universal_region_relations.outlives(r1, r2); + let r2_outlives_r1 = rcx.universal_region_relations.outlives(r2, r1); + match (r1_outlives_r2, r2_outlives_r1) { + (true, true) => r1.min(r2), + (true, false) => r2, + (false, true) => r1, + (false, false) => bug!("incomparable regions in total order"), + } + }) else { + debug!("no unique minimum choice"); + return; + }; + + debug!(?min_choice); + // Lift the member region to be at least as large as this `min_choice` by directly + // mutating the `scc_values` as we compute it. This acts as if we've added a + // `'member: 'min_choice` while not recomputing sccs. This means different sccs + // may now actually be equal. + let min_choice_scc = rcx.constraint_sccs.scc(min_choice); + rcx.scc_values.add_region(member, min_choice_scc); +} + +struct CollectMemberConstraintsVisitor<'a, 'b, 'tcx> { + rcx: &'a RegionCtxt<'a, 'tcx>, + defining_use: &'b DefiningUse<'tcx>, + member_constraints: &'a mut FxHashMap>>, +} +impl<'tcx> CollectMemberConstraintsVisitor<'_, '_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.rcx.infcx.tcx + } + fn visit_closure_args(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) { + let generics = self.cx().generics_of(def_id); + for arg in args.iter().skip(generics.parent_count) { + arg.visit_with(self); + } + } +} +impl<'tcx> TypeVisitor> for CollectMemberConstraintsVisitor<'_, '_, 'tcx> { + fn visit_region(&mut self, r: Region<'tcx>) { + match r.kind() { + ty::ReBound(..) => return, + ty::ReVar(vid) => { + let scc = self.rcx.constraint_sccs.scc(vid); + self.member_constraints.entry(scc).or_default().push(self.defining_use); + } + _ => unreachable!(), + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return; + } + + match *ty.kind() { + ty::Closure(def_id, args) + | ty::CoroutineClosure(def_id, args) + | ty::Coroutine(def_id, args) => self.visit_closure_args(def_id, args), + + ty::Alias(kind, ty::AliasTy { def_id, args, .. }) + if let Some(variances) = self.cx().opt_alias_variances(kind, def_id) => + { + // Skip lifetime parameters that are not captured, since they do + // not need member constraints registered for them; we'll erase + // them (and hopefully in the future replace them with placeholders). + for (&v, arg) in std::iter::zip(variances, args.iter()) { + if v != ty::Bivariant { + arg.visit_with(self) + } + } + } + + _ => ty.super_visit_with(self), + } + } +} diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs new file mode 100644 index 000000000000..33c4879af980 --- /dev/null +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -0,0 +1,698 @@ +use std::iter; +use std::rc::Rc; + +use rustc_data_structures::frozen::Frozen; +use rustc_data_structures::fx::FxIndexMap; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::outlives::env::RegionBoundPairs; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, OpaqueTypeStorageEntries}; +use rustc_infer::traits::ObligationCause; +use rustc_macros::extension; +use rustc_middle::mir::{Body, ConstraintCategory}; +use rustc_middle::ty::{ + self, DefiningScopeKind, FallibleTypeFolder, GenericArg, GenericArgsRef, OpaqueHiddenType, + OpaqueTypeKey, Region, RegionVid, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, + TypeVisitableExt, fold_regions, +}; +use rustc_mir_dataflow::points::DenseLocationMap; +use rustc_span::Span; +use rustc_trait_selection::opaque_types::{ + InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid, +}; +use rustc_trait_selection::solve::NoSolution; +use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; +use tracing::{debug, instrument}; + +use super::reverse_sccs::ReverseSccGraph; +use crate::consumers::RegionInferenceContext; +use crate::session_diagnostics::LifetimeMismatchOpaqueParam; +use crate::type_check::canonical::fully_perform_op_raw; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::type_check::{Locations, MirTypeckRegionConstraints}; +use crate::universal_regions::{RegionClassification, UniversalRegions}; +use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt}; + +mod member_constraints; +mod region_ctxt; + +use member_constraints::apply_member_constraints; +use region_ctxt::RegionCtxt; + +/// We defer errors from [fn handle_opaque_type_uses] and only report them +/// if there are no `RegionErrors`. If there are region errors, it's likely +/// that errors here are caused by them and don't need to be handled separately. +pub(crate) enum DeferredOpaqueTypeError<'tcx> { + InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>), + LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>), + UnexpectedHiddenRegion { + /// The opaque type. + opaque_type_key: OpaqueTypeKey<'tcx>, + /// The hidden type containing the member region. + hidden_type: OpaqueHiddenType<'tcx>, + /// The unexpected region. + member_region: Region<'tcx>, + }, + NonDefiningUseInDefiningScope { + span: Span, + opaque_type_key: OpaqueTypeKey<'tcx>, + }, +} + +/// This looks at all uses of opaque types in their defining scope inside +/// of this function. +/// +/// It first uses all defining uses to compute the actual concrete type of each +/// opaque type definition. +/// +/// We then apply this inferred type to actually check all uses of the opaque. +pub(crate) fn handle_opaque_type_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + universal_region_relations: &Frozen>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + location_map: &Rc, + constraints: &mut MirTypeckRegionConstraints<'tcx>, +) -> Vec> { + let tcx = infcx.tcx; + let opaque_types = infcx.clone_opaque_types(); + if opaque_types.is_empty() { + return Vec::new(); + } + + // We need to eagerly map all regions to NLL vars here, as we need to make sure we've + // introduced nll vars for all used placeholders. + // + // We need to resolve inference vars as even though we're in MIR typeck, we may still + // encounter inference variables, e.g. when checking user types. + let opaque_types_storage_num_entries = infcx.inner.borrow_mut().opaque_types().num_entries(); + let opaque_types = opaque_types + .into_iter() + .map(|entry| { + fold_regions(tcx, infcx.resolve_vars_if_possible(entry), |r, _| { + let vid = if let ty::RePlaceholder(placeholder) = r.kind() { + constraints.placeholder_region(infcx, placeholder).as_var() + } else { + universal_region_relations.universal_regions.to_region_vid(r) + }; + Region::new_var(tcx, vid) + }) + }) + .collect::>(); + + debug!(?opaque_types); + + let errors = compute_concrete_opaque_types( + root_cx, + infcx, + constraints, + universal_region_relations, + Rc::clone(location_map), + &opaque_types, + ); + + if !errors.is_empty() { + return errors; + } + + let errors = apply_computed_concrete_opaque_types( + root_cx, + infcx, + body, + &universal_region_relations.universal_regions, + region_bound_pairs, + known_type_outlives_obligations, + constraints, + &opaque_types, + ); + + detect_opaque_types_added_while_handling_opaque_types(infcx, opaque_types_storage_num_entries); + + errors +} + +/// Maps an NLL var to a deterministically chosen equal universal region. +/// +/// See the corresponding [rustc-dev-guide chapter] for more details. This +/// ignores changes to the region values due to member constraints. Applying +/// member constraints does not impact the result of this function. +/// +/// [rustc-dev-guide chapter]: https://rustc-dev-guide.rust-lang.org/borrow_check/opaque-types-region-inference-restrictions.html +fn nll_var_to_universal_region<'tcx>( + rcx: &RegionCtxt<'_, 'tcx>, + r: RegionVid, +) -> Option> { + // Use the SCC representative instead of directly using `region`. + // See [rustc-dev-guide chapter] § "Strict lifetime equality". + let vid = rcx.representative(r).rvid(); + match rcx.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 + // the same name and simplifies subsequent handling. + // See [rustc-dev-guide chapter] § "Semantic lifetime equality". + NllRegionVariableOrigin::FreeRegion => rcx + .universal_regions() + .universal_regions_iter() + .filter(|&ur| { + // See [rustc-dev-guide chapter] § "Closure restrictions". + !matches!( + rcx.universal_regions().region_classification(ur), + Some(RegionClassification::External) + ) + }) + .find(|&ur| rcx.universal_region_relations.equal(vid, ur)) + .map(|ur| rcx.definitions[ur].external_name.unwrap()), + NllRegionVariableOrigin::Placeholder(placeholder) => { + Some(ty::Region::new_placeholder(rcx.infcx.tcx, placeholder)) + } + // If `r` were equal to any universal region, its SCC representative + // would have been set to a free region. + NllRegionVariableOrigin::Existential { .. } => None, + } +} + +#[derive(Debug)] +struct DefiningUse<'tcx> { + /// The opaque type using non NLL vars. This uses the actual + /// free regions and placeholders. This is necessary + /// to interact with code outside of `rustc_borrowck`. + opaque_type_key: OpaqueTypeKey<'tcx>, + arg_regions: Vec, + hidden_type: OpaqueHiddenType<'tcx>, +} + +/// This computes the actual hidden types of the opaque types and maps them to their +/// definition sites. Outside of registering the computed concrete types this function +/// does not mutate the current borrowck state. +/// +/// While it may fail to infer the hidden type and return errors, we always apply +/// the computed concrete hidden type to all opaque type uses to check whether they +/// are correct. This is necessary to support non-defining uses of opaques in their +/// defining scope. +/// +/// It also means that this whole function is not really soundness critical as we +/// recheck all uses of the opaques regardless. +fn compute_concrete_opaque_types<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + constraints: &MirTypeckRegionConstraints<'tcx>, + universal_region_relations: &Frozen>, + location_map: Rc, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], +) -> Vec> { + let mut errors = Vec::new(); + // When computing the hidden type we need to track member constraints. + // We don't mutate the region graph used by `fn compute_regions` but instead + // manually track region information via a `RegionCtxt`. We discard this + // information at the end of this function. + let mut rcx = RegionCtxt::new(infcx, universal_region_relations, location_map, constraints); + + // We start by checking each use of an opaque type during type check and + // check whether the generic arguments of the opaque type are fully + // universal, if so, it's a defining use. + let defining_uses = collect_defining_uses(root_cx, &mut rcx, opaque_types, &mut errors); + + // We now compute and apply member constraints for all regions in the hidden + // types of each defining use. This mutates the region values of the `rcx` which + // is used when mapping the defining uses to the definition site. + apply_member_constraints(&mut rcx, &defining_uses); + + // After applying member constraints, we now check whether all member regions ended + // up equal to one of their choice regions and compute the actual concrete type of + // the opaque type definition. This is stored in the `root_cx`. + compute_concrete_types_from_defining_uses(root_cx, &rcx, &defining_uses, &mut errors); + errors +} + +#[instrument(level = "debug", skip_all, ret)] +fn collect_defining_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + rcx: &mut RegionCtxt<'_, 'tcx>, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], + errors: &mut Vec>, +) -> Vec> { + let infcx = rcx.infcx; + let mut defining_uses = vec![]; + for &(opaque_type_key, hidden_type) in opaque_types { + let non_nll_opaque_type_key = opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |r| { + nll_var_to_universal_region(&rcx, r.as_var()).unwrap_or(r) + }); + if let Err(err) = check_opaque_type_parameter_valid( + infcx, + non_nll_opaque_type_key, + hidden_type.span, + DefiningScopeKind::MirBorrowck, + ) { + // A non-defining use. This is a hard error on stable and gets ignored + // with `TypingMode::Borrowck`. + if infcx.tcx.use_typing_mode_borrowck() { + match err { + InvalidOpaqueTypeArgs::AlreadyReported(guar) => root_cx + .add_concrete_opaque_type( + opaque_type_key.def_id, + OpaqueHiddenType::new_error(infcx.tcx, guar), + ), + _ => debug!(?non_nll_opaque_type_key, ?err, "ignoring non-defining use"), + } + } else { + errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err)); + } + continue; + } + + // We use the original `opaque_type_key` to compute the `arg_regions`. + let arg_regions = iter::once(rcx.universal_regions().fr_static) + .chain( + opaque_type_key + .iter_captured_args(infcx.tcx) + .filter_map(|(_, arg)| arg.as_region()) + .map(Region::as_var), + ) + .collect(); + defining_uses.push(DefiningUse { + opaque_type_key: non_nll_opaque_type_key, + arg_regions, + hidden_type, + }); + } + + defining_uses +} + +fn compute_concrete_types_from_defining_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + rcx: &RegionCtxt<'_, 'tcx>, + defining_uses: &[DefiningUse<'tcx>], + errors: &mut Vec>, +) { + let infcx = rcx.infcx; + let tcx = infcx.tcx; + let mut decls_modulo_regions: FxIndexMap, (OpaqueTypeKey<'tcx>, Span)> = + FxIndexMap::default(); + for &DefiningUse { opaque_type_key, ref arg_regions, hidden_type } in defining_uses { + // After applying member constraints, we now map all regions in the hidden type + // to the `arg_regions` of this defining use. In case a region in the hidden type + // ended up not being equal to any such region, we error. + let hidden_type = + match hidden_type.try_fold_with(&mut ToArgRegionsFolder::new(rcx, arg_regions)) { + Ok(hidden_type) => hidden_type, + Err(r) => { + errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion { + hidden_type, + opaque_type_key, + member_region: ty::Region::new_var(tcx, r), + }); + let guar = tcx.dcx().span_delayed_bug( + hidden_type.span, + "opaque type with non-universal region args", + ); + ty::OpaqueHiddenType::new_error(tcx, guar) + } + }; + + // Now that we mapped the member regions to their final value, + // map the arguments of the opaque type key back to the parameters + // of the opaque type definition. + let ty = infcx + .infer_opaque_definition_from_instantiation(opaque_type_key, hidden_type) + .unwrap_or_else(|_| { + Ty::new_error_with_message( + rcx.infcx.tcx, + hidden_type.span, + "deferred invalid opaque type args", + ) + }); + + // Sometimes, when the hidden type is an inference variable, it can happen that + // the hidden type becomes the opaque type itself. In this case, this was an opaque + // usage of the opaque type and we can ignore it. This check is mirrored in typeck's + // writeback. + if !rcx.infcx.tcx.use_typing_mode_borrowck() { + if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() + && alias_ty.def_id == opaque_type_key.def_id.to_def_id() + && alias_ty.args == opaque_type_key.args + { + continue; + } + } + + // Check that all opaque types have the same region parameters if they have the same + // non-region parameters. This is necessary because within the new solver we perform + // various query operations modulo regions, and thus could unsoundly select some impls + // that don't hold. + // + // FIXME(-Znext-solver): This isn't necessary after all. We can remove this check again. + if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert( + rcx.infcx.tcx.erase_regions(opaque_type_key), + (opaque_type_key, hidden_type.span), + ) && let Some((arg1, arg2)) = std::iter::zip( + prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), + opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), + ) + .find(|(arg1, arg2)| arg1 != arg2) + { + errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam( + LifetimeMismatchOpaqueParam { + arg: arg1, + prev: arg2, + span: prev_span, + prev_span: hidden_type.span, + }, + )); + } + root_cx.add_concrete_opaque_type( + opaque_type_key.def_id, + OpaqueHiddenType { span: hidden_type.span, ty }, + ); + } +} + +/// A folder to map the regions in the hidden type to their corresponding `arg_regions`. +/// +/// This folder has to differentiate between member regions and other regions in the hidden +/// type. Member regions have to be equal to one of the `arg_regions` while other regions simply +/// get treated as an existential region in the opaque if they are not. Existential +/// regions are currently represented using `'erased`. +struct ToArgRegionsFolder<'a, 'tcx> { + rcx: &'a RegionCtxt<'a, 'tcx>, + // When folding closure args or bivariant alias arguments, we simply + // ignore non-member regions. However, we still need to map member + // regions to their arg region even if its in a closure argument. + // + // See tests/ui/type-alias-impl-trait/closure_wf_outlives.rs for an example. + erase_unknown_regions: bool, + arg_regions: &'a [RegionVid], +} + +impl<'a, 'tcx> ToArgRegionsFolder<'a, 'tcx> { + fn new( + rcx: &'a RegionCtxt<'a, 'tcx>, + arg_regions: &'a [RegionVid], + ) -> ToArgRegionsFolder<'a, 'tcx> { + ToArgRegionsFolder { rcx, erase_unknown_regions: false, arg_regions } + } + + fn fold_non_member_arg(&mut self, arg: GenericArg<'tcx>) -> GenericArg<'tcx> { + let prev = self.erase_unknown_regions; + self.erase_unknown_regions = true; + let res = arg.try_fold_with(self).unwrap(); + self.erase_unknown_regions = prev; + res + } + + fn fold_closure_args( + &mut self, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> Result, RegionVid> { + let generics = self.cx().generics_of(def_id); + self.cx().mk_args_from_iter(args.iter().enumerate().map(|(index, arg)| { + if index < generics.parent_count { + Ok(self.fold_non_member_arg(arg)) + } else { + arg.try_fold_with(self) + } + })) + } +} +impl<'tcx> FallibleTypeFolder> for ToArgRegionsFolder<'_, 'tcx> { + type Error = RegionVid; + fn cx(&self) -> TyCtxt<'tcx> { + self.rcx.infcx.tcx + } + + fn try_fold_region(&mut self, r: Region<'tcx>) -> Result, RegionVid> { + match r.kind() { + // ignore bound regions, keep visiting + ty::ReBound(_, _) => Ok(r), + _ => { + let r = r.as_var(); + if let Some(arg_region) = self + .arg_regions + .iter() + .copied() + .find(|&arg_vid| self.rcx.eval_equal(r, arg_vid)) + .and_then(|r| nll_var_to_universal_region(self.rcx, r)) + { + Ok(arg_region) + } else if self.erase_unknown_regions { + Ok(self.cx().lifetimes.re_erased) + } else { + Err(r) + } + } + } + } + + fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, RegionVid> { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return Ok(ty); + } + + let tcx = self.cx(); + Ok(match *ty.kind() { + ty::Closure(def_id, args) => { + Ty::new_closure(tcx, def_id, self.fold_closure_args(def_id, args)?) + } + + ty::CoroutineClosure(def_id, args) => { + Ty::new_coroutine_closure(tcx, def_id, self.fold_closure_args(def_id, args)?) + } + + ty::Coroutine(def_id, args) => { + Ty::new_coroutine(tcx, def_id, self.fold_closure_args(def_id, args)?) + } + + ty::Alias(kind, ty::AliasTy { def_id, args, .. }) + if let Some(variances) = tcx.opt_alias_variances(kind, def_id) => + { + let args = tcx.mk_args_from_iter(std::iter::zip(variances, args.iter()).map( + |(&v, s)| { + if v == ty::Bivariant { + Ok(self.fold_non_member_arg(s)) + } else { + s.try_fold_with(self) + } + }, + ))?; + ty::AliasTy::new_from_args(tcx, def_id, args).to_ty(tcx) + } + + _ => ty.try_super_fold_with(self)?, + }) + } +} + +/// This function is what actually applies member constraints to the borrowck +/// state. It is also responsible to check all uses of the opaques in their +/// defining scope. +/// +/// It does this by equating the hidden type of each use with the instantiated final +/// hidden type of the opaque. +fn apply_computed_concrete_opaque_types<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + constraints: &mut MirTypeckRegionConstraints<'tcx>, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], +) -> Vec> { + let tcx = infcx.tcx; + let mut errors = Vec::new(); + for &(key, hidden_type) in opaque_types { + let Some(expected) = root_cx.get_concrete_opaque_type(key.def_id) else { + assert!(tcx.use_typing_mode_borrowck(), "non-defining use in defining scope"); + errors.push(DeferredOpaqueTypeError::NonDefiningUseInDefiningScope { + span: hidden_type.span, + opaque_type_key: key, + }); + let guar = tcx.dcx().span_delayed_bug( + hidden_type.span, + "non-defining use in the defining scope with no defining uses", + ); + root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + continue; + }; + + // We erase all non-member region of the opaque and need to treat these as existentials. + let expected = ty::fold_regions(tcx, expected.instantiate(tcx, key.args), |re, _dbi| { + match re.kind() { + ty::ReErased => infcx.next_nll_region_var( + NllRegionVariableOrigin::Existential { name: None }, + || crate::RegionCtxt::Existential(None), + ), + _ => re, + } + }); + + // We now simply equate the expected with the actual hidden type. + let locations = Locations::All(hidden_type.span); + if let Err(guar) = fully_perform_op_raw( + infcx, + body, + universal_regions, + region_bound_pairs, + known_type_outlives_obligations, + constraints, + locations, + ConstraintCategory::OpaqueType, + CustomTypeOp::new( + |ocx| { + let cause = ObligationCause::misc( + hidden_type.span, + body.source.def_id().expect_local(), + ); + // We need to normalize both types in the old solver before equatingt them. + let actual_ty = ocx.normalize(&cause, infcx.param_env, hidden_type.ty); + let expected_ty = ocx.normalize(&cause, infcx.param_env, expected.ty); + ocx.eq(&cause, infcx.param_env, actual_ty, expected_ty).map_err(|_| NoSolution) + }, + "equating opaque types", + ), + ) { + root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + } + } + errors +} + +/// In theory `apply_concrete_opaque_types` could introduce new uses of opaque types. +/// We do not check these new uses so this could be unsound. +/// +/// We detect any new uses and simply delay a bug if they occur. If this results in +/// an ICE we can properly handle this, but we haven't encountered any such test yet. +/// +/// See the related comment in `FnCtxt::detect_opaque_types_added_during_writeback`. +fn detect_opaque_types_added_while_handling_opaque_types<'tcx>( + infcx: &InferCtxt<'tcx>, + opaque_types_storage_num_entries: OpaqueTypeStorageEntries, +) { + for (key, hidden_type) in infcx + .inner + .borrow_mut() + .opaque_types() + .opaque_types_added_since(opaque_types_storage_num_entries) + { + let opaque_type_string = infcx.tcx.def_path_str(key.def_id); + let msg = format!("unexpected cyclic definition of `{opaque_type_string}`"); + infcx.dcx().span_delayed_bug(hidden_type.span, msg); + } + + let _ = infcx.take_opaque_types(); +} + +impl<'tcx> RegionInferenceContext<'tcx> { + /// Map the regions in the type to named regions. This is similar to what + /// `infer_opaque_types` does, but can infer any universal region, not only + /// ones from the args for the opaque type. It also doesn't double check + /// that the regions produced are in fact equal to the named region they are + /// replaced with. This is fine because this function is only to improve the + /// region names in error messages. + /// + /// This differs from `MirBorrowckCtxt::name_regions` since it is particularly + /// lax with mapping region vids that are *shorter* than a universal region to + /// that universal region. This is useful for member region constraints since + /// we want to suggest a universal region name to capture even if it's technically + /// not equal to the error region. + pub(crate) fn name_regions_for_member_constraint(&self, tcx: TyCtxt<'tcx>, ty: T) -> T + where + T: TypeFoldable>, + { + fold_regions(tcx, ty, |region, _| match region.kind() { + ty::ReVar(vid) => { + let scc = self.constraint_sccs.scc(vid); + + // Special handling of higher-ranked regions. + if !self.max_nameable_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)) => { + return ty::Region::new_placeholder(tcx, placeholder); + } + + // Fallback: this will produce a cryptic error message. + _ => return region, + } + } + + // Find something that we can name + let upper_bound = self.approx_universal_upper_bound(vid); + if let Some(universal_region) = self.definitions[upper_bound].external_name { + return universal_region; + } + + // Nothing exact found, so we pick a named upper bound, if there's only one. + // If there's >1 universal region, then we probably are dealing w/ an intersection + // region which cannot be mapped back to a universal. + // FIXME: We could probably compute the LUB if there is one. + let scc = self.constraint_sccs.scc(vid); + let rev_scc_graph = + ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions()); + let upper_bounds: Vec<_> = rev_scc_graph + .upper_bounds(scc) + .filter_map(|vid| self.definitions[vid].external_name) + .filter(|r| !r.is_static()) + .collect(); + match &upper_bounds[..] { + [universal_region] => *universal_region, + _ => region, + } + } + _ => region, + }) + } +} + +#[extension(pub trait InferCtxtExt<'tcx>)] +impl<'tcx> InferCtxt<'tcx> { + /// Given the fully resolved, instantiated type for an opaque + /// type, i.e., the value of an inference variable like C1 or C2 + /// (*), computes the "definition type" for an opaque type + /// definition -- that is, the inferred value of `Foo1<'x>` or + /// `Foo2<'x>` that we would conceptually use in its definition: + /// ```ignore (illustrative) + /// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA + /// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// ``` + /// Note that these values are defined in terms of a distinct set of + /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main + /// purpose of this function is to do that translation. + /// + /// (*) C1 and C2 were introduced in the comments on + /// `register_member_constraints`. Read that comment for more context. + /// + /// # Parameters + /// + /// - `def_id`, the `impl Trait` type + /// - `args`, the args used to instantiate this opaque type + /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of + /// `opaque_defn.concrete_ty` + #[instrument(level = "debug", skip(self))] + fn infer_opaque_definition_from_instantiation( + &self, + opaque_type_key: OpaqueTypeKey<'tcx>, + instantiated_ty: OpaqueHiddenType<'tcx>, + ) -> Result, InvalidOpaqueTypeArgs<'tcx>> { + check_opaque_type_parameter_valid( + self, + opaque_type_key, + instantiated_ty.span, + DefiningScopeKind::MirBorrowck, + )?; + + let definition_ty = instantiated_ty + .remap_generic_params_to_declaration_params( + opaque_type_key, + self.tcx, + DefiningScopeKind::MirBorrowck, + ) + .ty; + + definition_ty.error_reported()?; + Ok(definition_ty) + } +} diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs new file mode 100644 index 000000000000..88326e4eebfc --- /dev/null +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs @@ -0,0 +1,114 @@ +use std::rc::Rc; + +use rustc_data_structures::frozen::Frozen; +use rustc_index::IndexVec; +use rustc_infer::infer::NllRegionVariableOrigin; +use rustc_middle::ty::{RegionVid, UniverseIndex}; +use rustc_mir_dataflow::points::DenseLocationMap; + +use crate::BorrowckInferCtxt; +use crate::constraints::ConstraintSccIndex; +use crate::handle_placeholders::{SccAnnotations, region_definitions}; +use crate::region_infer::reverse_sccs::ReverseSccGraph; +use crate::region_infer::values::RegionValues; +use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker, Representative}; +use crate::type_check::MirTypeckRegionConstraints; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::universal_regions::UniversalRegions; + +/// A slimmed down version of [crate::region_infer::RegionInferenceContext] used +/// only by opaque type handling. +pub(super) struct RegionCtxt<'a, 'tcx> { + pub(super) infcx: &'a BorrowckInferCtxt<'tcx>, + pub(super) definitions: Frozen>>, + pub(super) universal_region_relations: &'a UniversalRegionRelations<'tcx>, + pub(super) constraint_sccs: ConstraintSccs, + pub(super) scc_annotations: IndexVec, + pub(super) rev_scc_graph: ReverseSccGraph, + pub(super) scc_values: RegionValues, +} + +impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { + /// Creates a new `RegionCtxt` used to compute defining opaque type uses. + /// + /// This does not yet propagate region values. This is instead done lazily + /// when applying member constraints. + pub(super) fn new( + infcx: &'a BorrowckInferCtxt<'tcx>, + universal_region_relations: &'a Frozen>, + location_map: Rc, + constraints: &MirTypeckRegionConstraints<'tcx>, + ) -> RegionCtxt<'a, 'tcx> { + let universal_regions = &universal_region_relations.universal_regions; + let (definitions, _has_placeholders) = region_definitions(infcx, universal_regions); + let mut scc_annotations = SccAnnotations::init(&definitions); + let constraint_sccs = ConstraintSccs::new_with_annotation( + &constraints + .outlives_constraints + .graph(definitions.len()) + .region_graph(&constraints.outlives_constraints, universal_regions.fr_static), + &mut scc_annotations, + ); + let scc_annotations = scc_annotations.scc_to_annotation; + + // Unlike the `RegionInferenceContext`, we only care about free regions + // and fully ignore liveness and placeholders. + let placeholder_indices = Default::default(); + let mut scc_values = + RegionValues::new(location_map, universal_regions.len(), placeholder_indices); + for variable in definitions.indices() { + let scc = constraint_sccs.scc(variable); + match definitions[variable].origin { + NllRegionVariableOrigin::FreeRegion => { + scc_values.add_element(scc, variable); + } + _ => {} + } + } + + let rev_scc_graph = ReverseSccGraph::compute(&constraint_sccs, universal_regions); + RegionCtxt { + infcx, + definitions, + universal_region_relations, + constraint_sccs, + scc_annotations, + rev_scc_graph, + scc_values, + } + } + + pub(super) fn representative(&self, vid: RegionVid) -> Representative { + let scc = self.constraint_sccs.scc(vid); + self.scc_annotations[scc].representative + } + + pub(crate) fn max_placeholder_universe_reached( + &self, + scc: ConstraintSccIndex, + ) -> UniverseIndex { + self.scc_annotations[scc].max_placeholder_universe_reached() + } + + pub(super) fn universal_regions(&self) -> &UniversalRegions<'tcx> { + &self.universal_region_relations.universal_regions + } + + pub(super) fn eval_equal(&self, r1_vid: RegionVid, r2_vid: RegionVid) -> bool { + let r1 = self.constraint_sccs.scc(r1_vid); + let r2 = self.constraint_sccs.scc(r2_vid); + + if r1 == r2 { + return true; + } + + let universal_outlives = |sub, sup| { + self.scc_values.universal_regions_outlived_by(sub).all(|r1| { + self.scc_values + .universal_regions_outlived_by(sup) + .any(|r2| self.universal_region_relations.outlives(r2, r1)) + }) + }; + universal_outlives(r1, r2) && universal_outlives(r2, r1) + } +} diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs index 604265f89408..e8da85eccef1 100644 --- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs +++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs @@ -5,7 +5,6 @@ use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_middle::ty::RegionVid; -use crate::RegionInferenceContext; use crate::constraints::ConstraintSccIndex; use crate::region_infer::ConstraintSccs; use crate::universal_regions::UniversalRegions; @@ -57,12 +56,3 @@ pub(super) fn upper_bounds(&self, scc0: ConstraintSccIndex) -> impl Iterator { - /// Return the reverse graph of the region SCCs, initialising it if needed. - pub(super) fn reverse_scc_graph(&self) -> &ReverseSccGraph { - self.rev_scc_graph.get_or_init(|| { - ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions()) - }) - } -} diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index 9b1d12aede51..4e90ae391bb2 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; -use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{EarlyBinder, OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::ErrorGuaranteed; use smallvec::SmallVec; @@ -19,7 +19,7 @@ pub(super) struct BorrowCheckRootCtxt<'tcx> { tainted_by_errors: Option, /// This should be `None` during normal compilation. See [`crate::consumers`] for more /// information on how this is used. - pub(crate) consumer: Option>, + pub consumer: Option>, } impl<'tcx> BorrowCheckRootCtxt<'tcx> { @@ -38,6 +38,10 @@ pub(super) fn new( } } + pub(super) fn root_def_id(&self) -> LocalDefId { + self.root_def_id + } + /// Collect all defining uses of opaque types inside of this typeck root. This /// expects the hidden type to be mapped to the definition parameters of the opaque /// and errors if we end up with distinct hidden types. @@ -67,6 +71,13 @@ pub(super) fn add_concrete_opaque_type( } } + pub(super) fn get_concrete_opaque_type( + &mut self, + def_id: LocalDefId, + ) -> Option>> { + self.concrete_opaque_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty)) + } + pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) { self.tainted_by_errors = Some(guar); } diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index b3fa786a5177..2627ed899a93 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -2,8 +2,9 @@ use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_middle::bug; -use rustc_middle::mir::ConstraintCategory; +use rustc_middle::mir::{Body, ConstraintCategory}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast}; use rustc_span::Span; use rustc_span::def_id::DefId; @@ -14,7 +15,68 @@ use tracing::{debug, instrument}; use super::{Locations, NormalizeLocation, TypeChecker}; +use crate::BorrowckInferCtxt; use crate::diagnostics::ToUniverseInfo; +use crate::type_check::{MirTypeckRegionConstraints, constraint_conversion}; +use crate::universal_regions::UniversalRegions; + +#[instrument(skip(infcx, constraints, op), level = "trace")] +pub(crate) fn fully_perform_op_raw<'tcx, R: fmt::Debug, Op>( + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + constraints: &mut MirTypeckRegionConstraints<'tcx>, + locations: Locations, + category: ConstraintCategory<'tcx>, + op: Op, +) -> Result +where + Op: type_op::TypeOp<'tcx, Output = R>, + Op::ErrorInfo: ToUniverseInfo<'tcx>, +{ + let old_universe = infcx.universe(); + + let TypeOpOutput { output, constraints: query_constraints, error_info } = + op.fully_perform(infcx, infcx.root_def_id, locations.span(body))?; + if cfg!(debug_assertions) { + let data = infcx.take_and_reset_region_constraints(); + if !data.is_empty() { + panic!("leftover region constraints: {data:#?}"); + } + } + + debug!(?output, ?query_constraints); + + if let Some(data) = query_constraints { + constraint_conversion::ConstraintConversion::new( + infcx, + universal_regions, + region_bound_pairs, + known_type_outlives_obligations, + locations, + locations.span(body), + category, + constraints, + ) + .convert_all(data); + } + + // If the query has created new universes and errors are going to be emitted, register the + // cause of these new universes for improved diagnostics. + let universe = infcx.universe(); + if old_universe != universe + && let Some(error_info) = error_info + { + let universe_info = error_info.to_universe_info(old_universe); + for u in (old_universe + 1)..=universe { + constraints.universe_causes.insert(u, universe_info.clone()); + } + } + + Ok(output) +} impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// Given some operation `op` that manipulates types, proves @@ -38,36 +100,17 @@ pub(super) fn fully_perform_op( Op: type_op::TypeOp<'tcx, Output = R>, Op::ErrorInfo: ToUniverseInfo<'tcx>, { - let old_universe = self.infcx.universe(); - - let TypeOpOutput { output, constraints, error_info } = - op.fully_perform(self.infcx, locations.span(self.body))?; - if cfg!(debug_assertions) { - let data = self.infcx.take_and_reset_region_constraints(); - if !data.is_empty() { - panic!("leftover region constraints: {data:#?}"); - } - } - - debug!(?output, ?constraints); - - if let Some(data) = constraints { - self.push_region_constraints(locations, category, data); - } - - // If the query has created new universes and errors are going to be emitted, register the - // cause of these new universes for improved diagnostics. - let universe = self.infcx.universe(); - if old_universe != universe - && let Some(error_info) = error_info - { - let universe_info = error_info.to_universe_info(old_universe); - for u in (old_universe + 1)..=universe { - self.constraints.universe_causes.insert(u, universe_info.clone()); - } - } - - Ok(output) + fully_perform_op_raw( + self.infcx, + self.body, + self.universal_regions, + self.region_bound_pairs, + self.known_type_outlives_obligations, + self.constraints, + locations, + category, + op, + ) } pub(super) fn instantiate_canonical( diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 9bb96b94506a..703223e2e54a 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -1,10 +1,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LocalDefId; +use rustc_infer::infer::SubregionOrigin; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; -use rustc_infer::infer::{InferCtxt, SubregionOrigin}; use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_middle::bug; use rustc_middle::ty::{ @@ -18,10 +18,12 @@ use crate::region_infer::TypeTest; use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::UniversalRegions; -use crate::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory}; +use crate::{ + BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory, +}; pub(crate) struct ConstraintConversion<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, + infcx: &'a BorrowckInferCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, /// Each RBP `GK: 'a` is assumed to be true. These encode /// relationships like `T: 'a` that are added via implicit bounds @@ -34,7 +36,6 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { /// logic expecting to see (e.g.) `ReStatic`, and if we supplied /// our special inference variable there, we would mess that up. region_bound_pairs: &'a RegionBoundPairs<'tcx>, - param_env: ty::ParamEnv<'tcx>, known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, span: Span, @@ -45,10 +46,9 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { pub(crate) fn new( - infcx: &'a InferCtxt<'tcx>, + infcx: &'a BorrowckInferCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, - param_env: ty::ParamEnv<'tcx>, known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, span: Span, @@ -59,7 +59,6 @@ pub(crate) fn new( infcx, universal_regions, region_bound_pairs, - param_env, known_type_outlives_obligations, locations, span, @@ -286,8 +285,11 @@ fn normalize_and_add_type_outlives_constraints( ConstraintCategory<'tcx>, )>, ) -> Ty<'tcx> { - match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span) - { + match self.infcx.param_env.and(DeeplyNormalize { value: ty }).fully_perform( + self.infcx, + self.infcx.root_def_id, + self.span, + ) { Ok(TypeOpOutput { output: ty, constraints, .. }) => { // FIXME(higher_ranked_auto): What should we do with the assumptions here? if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints { diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index f642d34ea673..7bf2df914707 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -2,9 +2,9 @@ use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; use rustc_hir::def::DefKind; use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::outlives; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::GenericKind; -use rustc_infer::infer::{InferCtxt, outlives}; use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; @@ -14,6 +14,7 @@ use tracing::{debug, instrument}; use type_op::TypeOpOutput; +use crate::BorrowckInferCtxt; use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion}; use crate::universal_regions::UniversalRegions; @@ -47,14 +48,12 @@ pub(crate) struct CreateResult<'tcx> { } pub(crate) fn create<'tcx>( - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, universal_regions: UniversalRegions<'tcx>, constraints: &mut MirTypeckRegionConstraints<'tcx>, ) -> CreateResult<'tcx> { UniversalRegionRelationsBuilder { infcx, - param_env, constraints, universal_regions, region_bound_pairs: Default::default(), @@ -177,8 +176,7 @@ pub(crate) fn known_outlives(&self) -> impl Iterator { - infcx: &'a InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + infcx: &'a BorrowckInferCtxt<'tcx>, universal_regions: UniversalRegions<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, @@ -205,7 +203,7 @@ pub(crate) fn create(mut self) -> CreateResult<'tcx> { // Insert the `'a: 'b` we know from the predicates. // This does not consider the type-outlives. - let param_env = self.param_env; + let param_env = self.infcx.param_env; self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env)); // - outlives is reflexive, so `'r: 'r` for every region `'r` @@ -263,7 +261,7 @@ pub(crate) fn create(mut self) -> CreateResult<'tcx> { let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = param_env .and(DeeplyNormalize { value: ty }) - .fully_perform(self.infcx, span) + .fully_perform(self.infcx, self.infcx.root_def_id, span) .unwrap_or_else(|guar| TypeOpOutput { output: Ty::new_error(self.infcx.tcx, guar), constraints: None, @@ -298,8 +296,9 @@ pub(crate) fn create(mut self) -> CreateResult<'tcx> { // Add implied bounds from impl header. if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { - let result: Result<_, ErrorGuaranteed> = - param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, span); + let result: Result<_, ErrorGuaranteed> = param_env + .and(DeeplyNormalize { value: ty }) + .fully_perform(self.infcx, self.infcx.root_def_id, span); let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { continue; }; @@ -318,7 +317,6 @@ pub(crate) fn create(mut self) -> CreateResult<'tcx> { self.infcx, &self.universal_regions, &self.region_bound_pairs, - param_env, &known_type_outlives_obligations, Locations::All(span), span, @@ -353,10 +351,11 @@ fn normalize_and_push_type_outlives_obligation( output: normalized_outlives, constraints: constraints_normalize, error_info: _, - }) = self - .param_env - .and(DeeplyNormalize { value: outlives }) - .fully_perform(self.infcx, span) + }) = self.infcx.param_env.and(DeeplyNormalize { value: outlives }).fully_perform( + self.infcx, + self.infcx.root_def_id, + span, + ) else { self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}")); return; @@ -381,9 +380,10 @@ fn add_implied_bounds( span: Span, ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { let TypeOpOutput { output: bounds, constraints, .. } = self + .infcx .param_env .and(type_op::ImpliedOutlivesBounds { ty }) - .fully_perform(self.infcx, span) + .fully_perform(self.infcx, self.infcx.root_def_id, span) .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; debug!(?bounds, ?constraints); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 5d30fa71e92c..b704d8f0a769 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -640,7 +640,7 @@ fn compute_drop_data( let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty }); - match op.fully_perform(typeck.infcx, DUMMY_SP) { + match op.fully_perform(typeck.infcx, typeck.root_cx.root_def_id(), DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { DropData { dropck_result: output, region_constraint_data: constraints } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index a960b96b91c2..e55f5b7b43ef 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -26,8 +26,7 @@ use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::{ self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt, - GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, TypeVisitableExt, - UserArgs, UserTypeAnnotationIndex, fold_regions, + GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::move_paths::MoveData; @@ -42,7 +41,6 @@ use crate::borrow_set::BorrowSet; use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet}; use crate::diagnostics::UniverseInfo; -use crate::member_constraints::MemberConstraintSet; use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable}; use crate::polonius::{PoloniusContext, PoloniusLivenessContext}; use crate::region_infer::TypeTest; @@ -67,12 +65,11 @@ macro_rules! span_mirbug { }) } -mod canonical; +pub(crate) mod canonical; mod constraint_conversion; pub(crate) mod free_region_relations; mod input_output; pub(crate) mod liveness; -mod opaque_types; mod relate_tys; /// Type checks the given `mir` in the context of the inference @@ -114,7 +111,6 @@ pub(crate) fn type_check<'tcx>( placeholder_index_to_region: IndexVec::default(), liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&location_map)), outlives_constraints: OutlivesConstraintSet::default(), - member_constraints: MemberConstraintSet::default(), type_tests: Vec::default(), universe_causes: FxIndexMap::default(), }; @@ -124,7 +120,7 @@ pub(crate) fn type_check<'tcx>( region_bound_pairs, normalized_inputs_and_output, known_type_outlives_obligations, - } = free_region_relations::create(infcx, infcx.param_env, universal_regions, &mut constraints); + } = free_region_relations::create(infcx, universal_regions, &mut constraints); let pre_obligations = infcx.take_registered_region_obligations(); assert!( @@ -170,9 +166,6 @@ pub(crate) fn type_check<'tcx>( liveness::generate(&mut typeck, &location_map, move_data); - let opaque_type_values = - opaque_types::take_opaques_and_register_member_constraints(&mut typeck); - // We're done with typeck, we can finalize the polonius liveness context for region inference. let polonius_context = typeck.polonius_liveness.take().map(|liveness_context| { PoloniusContext::create_from_liveness( @@ -187,7 +180,6 @@ pub(crate) fn type_check<'tcx>( if let Some(guar) = universal_region_relations.universal_regions.encountered_re_error() { debug!("encountered an error region; removing constraints!"); constraints.outlives_constraints = Default::default(); - constraints.member_constraints = Default::default(); constraints.type_tests = Default::default(); root_cx.set_tainted_by_errors(guar); infcx.set_tainted_by_errors(guar); @@ -196,7 +188,8 @@ pub(crate) fn type_check<'tcx>( MirTypeckResults { constraints, universal_region_relations, - opaque_type_values, + region_bound_pairs, + known_type_outlives_obligations, polonius_context, } } @@ -245,7 +238,8 @@ struct TypeChecker<'a, 'tcx> { pub(crate) struct MirTypeckResults<'tcx> { pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, pub(crate) universal_region_relations: Frozen>, - pub(crate) opaque_type_values: FxIndexMap, OpaqueHiddenType<'tcx>>, + pub(crate) region_bound_pairs: Frozen>, + pub(crate) known_type_outlives_obligations: Frozen>>, pub(crate) polonius_context: Option, } @@ -277,8 +271,6 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, - pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, - pub(crate) universe_causes: FxIndexMap>, pub(crate) type_tests: Vec>, @@ -287,7 +279,7 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { impl<'tcx> MirTypeckRegionConstraints<'tcx> { /// Creates a `Region` for a given `PlaceholderRegion`, or returns the /// region that corresponds to a previously created one. - fn placeholder_region( + pub(crate) fn placeholder_region( &mut self, infcx: &InferCtxt<'tcx>, placeholder: ty::PlaceholderRegion, @@ -380,14 +372,6 @@ fn body(&self) -> &Body<'tcx> { self.body } - fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid { - if let ty::RePlaceholder(placeholder) = r.kind() { - self.constraints.placeholder_region(self.infcx, placeholder).as_var() - } else { - self.universal_regions.to_region_vid(r) - } - } - fn unsized_feature_enabled(&self) -> bool { self.tcx().features().unsized_fn_params() } @@ -424,7 +408,6 @@ fn push_region_constraints( self.infcx, self.universal_regions, self.region_bound_pairs, - self.infcx.param_env, self.known_type_outlives_obligations, locations, locations.span(self.body), @@ -1895,7 +1878,7 @@ fn check_call_dest( if !output_ty .is_privately_uninhabited(self.tcx(), self.infcx.typing_env(self.infcx.param_env)) { - span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); + span_mirbug!(self, term, "call to non-diverging function {:?} w/o dest", sig); } } else { let dest_ty = destination.ty(self.body, tcx).ty; @@ -2474,12 +2457,12 @@ fn prove_closure_bounds( args: GenericArgsRef<'tcx>, locations: Locations, ) -> ty::InstantiatedPredicates<'tcx> { + let root_def_id = self.root_cx.root_def_id(); if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) { constraint_conversion::ConstraintConversion::new( self.infcx, self.universal_regions, self.region_bound_pairs, - self.infcx.param_env, self.known_type_outlives_obligations, locations, self.body.span, // irrelevant; will be overridden. @@ -2489,9 +2472,8 @@ fn prove_closure_bounds( .apply_closure_requirements(closure_requirements, def_id, args); } - // Now equate closure args to regions inherited from `typeck_root_def_id`. Fixes #98589. - let typeck_root_def_id = tcx.typeck_root_def_id(self.body.source.def_id()); - let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id); + // Now equate closure args to regions inherited from `root_def_id`. Fixes #98589. + let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, root_def_id); let parent_args = match tcx.def_kind(def_id) { // We don't want to dispatch on 3 different kind of closures here, so take @@ -2566,17 +2548,14 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> { fn fully_perform( mut self, infcx: &InferCtxt<'tcx>, + root_def_id: LocalDefId, span: Span, ) -> Result, ErrorGuaranteed> { - let (mut output, region_constraints) = scrape_region_constraints( - infcx, - |ocx| { + let (mut output, region_constraints) = + scrape_region_constraints(infcx, root_def_id, "InstantiateOpaqueType", span, |ocx| { ocx.register_obligations(self.obligations.clone()); Ok(()) - }, - "InstantiateOpaqueType", - span, - )?; + })?; self.region_constraints = Some(region_constraints); output.error_info = Some(self); Ok(output) diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs deleted file mode 100644 index 5a422483eef4..000000000000 --- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs +++ /dev/null @@ -1,333 +0,0 @@ -use std::iter; - -use rustc_data_structures::fx::FxIndexMap; -use rustc_middle::span_bug; -use rustc_middle::ty::{ - self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, fold_regions, -}; -use tracing::{debug, trace}; - -use super::{MemberConstraintSet, TypeChecker}; - -/// Once we're done with typechecking the body, we take all the opaque types -/// defined by this function and add their 'member constraints'. -pub(super) fn take_opaques_and_register_member_constraints<'tcx>( - typeck: &mut TypeChecker<'_, 'tcx>, -) -> FxIndexMap, OpaqueHiddenType<'tcx>> { - let infcx = typeck.infcx; - // Annoying: to invoke `typeck.to_region_vid`, we need access to - // `typeck.constraints`, but we also want to be mutating - // `typeck.member_constraints`. For now, just swap out the value - // we want and replace at the end. - let mut member_constraints = std::mem::take(&mut typeck.constraints.member_constraints); - let opaque_types = infcx - .take_opaque_types() - .into_iter() - .map(|(opaque_type_key, hidden_type)| { - let hidden_type = infcx.resolve_vars_if_possible(hidden_type); - register_member_constraints( - typeck, - &mut member_constraints, - opaque_type_key, - hidden_type, - ); - trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind()); - if hidden_type.has_non_region_infer() { - span_bug!(hidden_type.span, "could not resolve {:?}", hidden_type.ty); - } - - // Convert all regions to nll vars. - let (opaque_type_key, hidden_type) = - fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |r, _| { - ty::Region::new_var(infcx.tcx, typeck.to_region_vid(r)) - }); - - (opaque_type_key, hidden_type) - }) - .collect(); - assert!(typeck.constraints.member_constraints.is_empty()); - typeck.constraints.member_constraints = member_constraints; - opaque_types -} - -/// Given the map `opaque_types` containing the opaque -/// `impl Trait` types whose underlying, hidden types are being -/// inferred, this method adds constraints to the regions -/// appearing in those underlying hidden types to ensure that they -/// at least do not refer to random scopes within the current -/// function. These constraints are not (quite) sufficient to -/// guarantee that the regions are actually legal values; that -/// final condition is imposed after region inference is done. -/// -/// # The Problem -/// -/// Let's work through an example to explain how it works. Assume -/// the current function is as follows: -/// -/// ```text -/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) -/// ``` -/// -/// Here, we have two `impl Trait` types whose values are being -/// inferred (the `impl Bar<'a>` and the `impl -/// Bar<'b>`). Conceptually, this is sugar for a setup where we -/// define underlying opaque types (`Foo1`, `Foo2`) and then, in -/// the return type of `foo`, we *reference* those definitions: -/// -/// ```text -/// type Foo1<'x> = impl Bar<'x>; -/// type Foo2<'x> = impl Bar<'x>; -/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } -/// // ^^^^ ^^ -/// // | | -/// // | args -/// // def_id -/// ``` -/// -/// As indicating in the comments above, each of those references -/// is (in the compiler) basically generic parameters (`args`) -/// applied to the type of a suitable `def_id` (which identifies -/// `Foo1` or `Foo2`). -/// -/// Now, at this point in compilation, what we have done is to -/// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with -/// fresh inference variables C1 and C2. We wish to use the values -/// of these variables to infer the underlying types of `Foo1` and -/// `Foo2`. That is, this gives rise to higher-order (pattern) unification -/// constraints like: -/// -/// ```text -/// for<'a> (Foo1<'a> = C1) -/// for<'b> (Foo1<'b> = C2) -/// ``` -/// -/// For these equation to be satisfiable, the types `C1` and `C2` -/// can only refer to a limited set of regions. For example, `C1` -/// can only refer to `'static` and `'a`, and `C2` can only refer -/// to `'static` and `'b`. The job of this function is to impose that -/// constraint. -/// -/// Up to this point, C1 and C2 are basically just random type -/// inference variables, and hence they may contain arbitrary -/// regions. In fact, it is fairly likely that they do! Consider -/// this possible definition of `foo`: -/// -/// ```text -/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { -/// (&*x, &*y) -/// } -/// ``` -/// -/// Here, the values for the concrete types of the two impl -/// traits will include inference variables: -/// -/// ```text -/// &'0 i32 -/// &'1 i32 -/// ``` -/// -/// Ordinarily, the subtyping rules would ensure that these are -/// sufficiently large. But since `impl Bar<'a>` isn't a specific -/// type per se, we don't get such constraints by default. This -/// is where this function comes into play. It adds extra -/// constraints to ensure that all the regions which appear in the -/// inferred type are regions that could validly appear. -/// -/// This is actually a bit of a tricky constraint in general. We -/// want to say that each variable (e.g., `'0`) can only take on -/// values that were supplied as arguments to the opaque type -/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in -/// scope. We don't have a constraint quite of this kind in the current -/// region checker. -/// -/// # The Solution -/// -/// We generally prefer to make `<=` constraints, since they -/// integrate best into the region solver. To do that, we find the -/// "minimum" of all the arguments that appear in the args: that -/// is, some region which is less than all the others. In the case -/// of `Foo1<'a>`, that would be `'a` (it's the only choice, after -/// all). Then we apply that as a least bound to the variables -/// (e.g., `'a <= '0`). -/// -/// In some cases, there is no minimum. Consider this example: -/// -/// ```text -/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } -/// ``` -/// -/// Here we would report a more complex "in constraint", like `'r -/// in ['a, 'b, 'static]` (where `'r` is some region appearing in -/// the hidden type). -/// -/// # Constrain regions, not the hidden concrete type -/// -/// Note that generating constraints on each region `Rc` is *not* -/// the same as generating an outlives constraint on `Tc` itself. -/// For example, if we had a function like this: -/// -/// ``` -/// # #![feature(type_alias_impl_trait)] -/// # fn main() {} -/// # trait Foo<'a> {} -/// # impl<'a, T> Foo<'a> for (&'a u32, T) {} -/// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { -/// (x, y) -/// } -/// -/// // Equivalent to: -/// # mod dummy { use super::*; -/// type FooReturn<'a, T> = impl Foo<'a>; -/// #[define_opaque(FooReturn)] -/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> { -/// (x, y) -/// } -/// # } -/// ``` -/// -/// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` -/// is an inference variable). If we generated a constraint that -/// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- -/// but this is not necessary, because the opaque type we -/// create will be allowed to reference `T`. So we only generate a -/// constraint that `'0: 'a`. -fn register_member_constraints<'tcx>( - typeck: &mut TypeChecker<'_, 'tcx>, - member_constraints: &mut MemberConstraintSet<'tcx, ty::RegionVid>, - opaque_type_key: OpaqueTypeKey<'tcx>, - OpaqueHiddenType { span, ty: hidden_ty }: OpaqueHiddenType<'tcx>, -) { - let tcx = typeck.tcx(); - let hidden_ty = typeck.infcx.resolve_vars_if_possible(hidden_ty); - debug!(?hidden_ty); - - let variances = tcx.variances_of(opaque_type_key.def_id); - debug!(?variances); - - // For a case like `impl Foo<'a, 'b>`, we would generate a constraint - // `'r in ['a, 'b, 'static]` for each region `'r` that appears in the - // hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`). - // - // `conflict1` and `conflict2` are the two region bounds that we - // detected which were unrelated. They are used for diagnostics. - - // Create the set of choice regions: each region in the hidden - // type can be equal to any of the region parameters of the - // opaque type definition. - let fr_static = typeck.universal_regions.fr_static; - let choice_regions: Vec<_> = opaque_type_key - .args - .iter() - .enumerate() - .filter(|(i, _)| variances[*i] == ty::Invariant) - .filter_map(|(_, arg)| match arg.kind() { - GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)), - GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, - }) - .chain(iter::once(fr_static)) - .collect(); - - // FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's - // not currently sound until we have existential regions. - hidden_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx, - op: |r| { - member_constraints.add_member_constraint( - opaque_type_key, - hidden_ty, - span, - typeck.to_region_vid(r), - &choice_regions, - ) - }, - }); -} - -/// Visitor that requires that (almost) all regions in the type visited outlive -/// `least_region`. We cannot use `push_outlives_components` because regions in -/// closure signatures are not included in their outlives components. We need to -/// ensure all regions outlive the given bound so that we don't end up with, -/// say, `ReVar` appearing in a return type and causing ICEs when other -/// functions end up with region constraints involving regions from other -/// functions. -/// -/// We also cannot use `for_each_free_region` because for closures it includes -/// the regions parameters from the enclosing item. -/// -/// We ignore any type parameters because impl trait values are assumed to -/// capture all the in-scope type parameters. -struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> { - tcx: TyCtxt<'tcx>, - op: OP, -} - -impl<'tcx, OP> TypeVisitor> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> -where - OP: FnMut(ty::Region<'tcx>), -{ - fn visit_region(&mut self, r: ty::Region<'tcx>) { - match r.kind() { - // ignore bound regions, keep visiting - ty::ReBound(_, _) => {} - _ => (self.op)(r), - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) { - // We're only interested in types involving regions - if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { - return; - } - - match *ty.kind() { - ty::Closure(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - - for upvar in args.as_closure().upvar_tys() { - upvar.visit_with(self); - } - args.as_closure().sig_as_fn_ptr_ty().visit_with(self); - } - - ty::CoroutineClosure(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - - for upvar in args.as_coroutine_closure().upvar_tys() { - upvar.visit_with(self); - } - - args.as_coroutine_closure().signature_parts_ty().visit_with(self); - } - - ty::Coroutine(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - // Also skip the witness type, because that has no free regions. - - for upvar in args.as_coroutine().upvar_tys() { - upvar.visit_with(self); - } - args.as_coroutine().return_ty().visit_with(self); - args.as_coroutine().yield_ty().visit_with(self); - args.as_coroutine().resume_ty().visit_with(self); - } - - ty::Alias(kind, ty::AliasTy { def_id, args, .. }) - if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) => - { - // Skip lifetime parameters that are not captured, since they do - // not need member constraints registered for them; we'll erase - // them (and hopefully in the future replace them with placeholders). - for (v, s) in std::iter::zip(variances, args.iter()) { - if *v != ty::Bivariant { - s.visit_with(self); - } - } - } - - _ => { - ty.super_visit_with(self); - } - } - } -} diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 84ca9bad2c14..7ac2dff12f75 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -124,8 +124,13 @@ fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> // by using `ty_vid rel B` and then finally and end by equating `ty_vid` to // the opaque. let mut enable_subtyping = |ty, opaque_is_expected| { - let ty_vid = infcx.next_ty_var_id_in_universe(self.span(), ty::UniverseIndex::ROOT); - + // We create the fresh inference variable in the highest universe. + // In theory we could limit it to the highest universe in the args of + // the opaque but that isn't really worth the effort. + // + // We'll make sure that the opaque type can actually name everything + // in its hidden type later on. + let ty_vid = infcx.next_ty_vid(self.span()); let variance = if opaque_is_expected { self.ambient_variance } else { diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index f7d8f4aa7835..48d80004cdd6 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -1,9 +1,9 @@ //! Implementation of the `#[cfg_accessible(path)]` attribute macro. use rustc_ast as ast; +use rustc_attr_parsing::validate_attr; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; use rustc_feature::AttributeTemplate; -use rustc_parse::validate_attr; use rustc_span::{Span, sym}; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index a33eca43de59..09d827b0635e 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -1,10 +1,10 @@ use rustc_ast as ast; use rustc_ast::{GenericParamKind, ItemKind, MetaItemInner, MetaItemKind, StmtKind}; +use rustc_attr_parsing::validate_attr; use rustc_expand::base::{ Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier, }; use rustc_feature::AttributeTemplate; -use rustc_parse::validate_attr; use rustc_session::Session; use rustc_span::{ErrorGuaranteed, Ident, Span, sym}; diff --git a/compiler/rustc_builtin_macros/src/deriving/from.rs b/compiler/rustc_builtin_macros/src/deriving/from.rs index ef0e6ca324a3..ab25de7c9175 100644 --- a/compiler/rustc_builtin_macros/src/deriving/from.rs +++ b/compiler/rustc_builtin_macros/src/deriving/from.rs @@ -27,21 +27,39 @@ pub(crate) fn expand_deriving_from( cx.dcx().bug("derive(From) used on something else than an item"); }; - // #[derive(From)] is currently usable only on structs with exactly one field. - let field = if let ItemKind::Struct(_, _, data) = &item.kind - && let [field] = data.fields() - { - Some(field.clone()) - } else { - None + let err_span = || { + let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span); + MultiSpan::from_spans(vec![span, item_span]) }; - let from_type = match &field { - Some(field) => Ty::AstTy(field.ty.clone()), - // We don't have a type to put into From<...> if we don't have a single field, so just put - // unit there. - None => Ty::Unit, + // `#[derive(From)]` is currently usable only on structs with exactly one field. + let field = match &item.kind { + ItemKind::Struct(_, _, data) => { + if let [field] = data.fields() { + Ok(field.clone()) + } else { + let guar = cx.dcx().emit_err(errors::DeriveFromWrongFieldCount { + span: err_span(), + multiple_fields: data.fields().len() > 1, + }); + Err(guar) + } + } + ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => { + let guar = cx.dcx().emit_err(errors::DeriveFromWrongTarget { + span: err_span(), + kind: &format!("{} {}", item.kind.article(), item.kind.descr()), + }); + Err(guar) + } + _ => cx.dcx().bug("Invalid derive(From) ADT input"), }; + + let from_type = Ty::AstTy(match field { + Ok(ref field) => field.ty.clone(), + Err(guar) => cx.ty(span, ast::TyKind::Err(guar)), + }); + let path = Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std); @@ -71,34 +89,17 @@ pub(crate) fn expand_deriving_from( attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Default, combine_substructure: combine_substructure(Box::new(|cx, span, substructure| { - let Some(field) = &field else { - let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span); - let err_span = MultiSpan::from_spans(vec![span, item_span]); - let error = match &item.kind { - ItemKind::Struct(_, _, data) => { - cx.dcx().emit_err(errors::DeriveFromWrongFieldCount { - span: err_span, - multiple_fields: data.fields().len() > 1, - }) - } - ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => { - cx.dcx().emit_err(errors::DeriveFromWrongTarget { - span: err_span, - kind: &format!("{} {}", item.kind.article(), item.kind.descr()), - }) - } - _ => cx.dcx().bug("Invalid derive(From) ADT input"), - }; - - return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(error))); + let field = match field { + Ok(ref field) => field, + Err(guar) => { + return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(guar))); + } }; let self_kw = Ident::new(kw::SelfUpper, span); let expr: Box = match substructure.fields { SubstructureFields::StaticStruct(variant, _) => match variant { - // Self { - // field: value - // } + // Self { field: value } VariantData::Struct { .. } => cx.expr_struct_ident( span, self_kw, diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index ec613b7b7103..6415e55e0b03 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -10,11 +10,12 @@ }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, pluralize, + Applicability, BufferedEarlyLint, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, + pluralize, }; use rustc_expand::base::*; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; -use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId}; +use rustc_lint_defs::{BuiltinLintDiag, LintId}; use rustc_parse::exp; use rustc_parse_format as parse; use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol}; @@ -595,7 +596,8 @@ enum ArgRef<'a> { named_arg_sp: arg_name.span, named_arg_name: arg_name.name.to_string(), is_formatting_arg: matches!(used_as, Width | Precision), - }, + } + .into(), }); } } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 86a4927f3903..1bcea95fbb7b 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -8,7 +8,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] -#![feature(autodiff)] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index e803f3be82b9..a9d91f77560a 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -141,7 +141,7 @@ fn visit_item(&mut self, item: &mut ast::Item) { if let ast::ItemKind::Mod( _, _, - ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. }, _), + ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. }), ) = item.kind { let prev_tests = mem::take(&mut self.tests); diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index f00c170e4850..3a4585d5be9d 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -1,12 +1,13 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{self as ast, AttrStyle, Attribute, MetaItem, attr, token}; +use rustc_attr_parsing::validate_attr; 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::BuiltinLintDiag; use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; -use rustc_parse::{exp, parser, validate_attr}; +use rustc_parse::{exp, parser}; use rustc_session::errors::report_lit_error; use rustc_span::{BytePos, Span, Symbol}; diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 043123fcab2c..399f8b6e7628 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -42,12 +42,13 @@ fn apply_attrs_to_callsite( const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] = [(ArgAttribute::InReg, llvm::AttributeKind::InReg)]; -const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 5] = [ +const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [ (ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias), (ArgAttribute::NoCapture, llvm::AttributeKind::NoCapture), (ArgAttribute::NonNull, llvm::AttributeKind::NonNull), (ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly), (ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef), + (ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly), ]; fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> { @@ -83,6 +84,10 @@ fn apply_attrs_to_callsite( } for (attr, llattr) in OPTIMIZATION_ATTRIBUTES { if regular.contains(attr) { + // captures(address, read_provenance) is only available since LLVM 21. + if attr == ArgAttribute::CapturesReadOnly && llvm_util::get_version() < (21, 0, 0) { + continue; + } attrs.push(llattr.create_attr(cx.llcx)); } } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index a6daacd95ef8..5affb26483aa 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -420,6 +420,16 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED) { to_add.push(create_alloc_family_attr(cx.llcx)); + if let Some(zv) = + cx.tcx.get_attr(instance.def_id(), rustc_span::sym::rustc_allocator_zeroed_variant) + && let Some(name) = zv.value_str() + { + to_add.push(llvm::CreateAttrStringValue( + cx.llcx, + "alloc-variant-zeroed", + &mangle_internal_symbol(cx.tcx, name.as_str()), + )); + } // apply to argument place instead of function let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]); @@ -497,7 +507,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module)); let name = - codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id())); + codegen_fn_attrs.symbol_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id())); let name = name.as_str(); to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name)); } diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 0a161442933a..7a340ae83f3d 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -1,104 +1,21 @@ //! A helper class for dealing with static archives -use std::ffi::{CStr, CString, c_char, c_void}; -use std::path::{Path, PathBuf}; -use std::{io, mem, ptr, str}; +use std::ffi::{CStr, c_char, c_void}; +use std::io; use rustc_codegen_ssa::back::archive::{ - ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, - DEFAULT_OBJECT_READER, ObjectReader, UnknownArchiveKind, try_extract_macho_fat_archive, + ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, ObjectReader, }; use rustc_session::Session; -use crate::llvm::archive_ro::{ArchiveRO, Child}; -use crate::llvm::{self, ArchiveKind, last_error}; - -/// Helper for adding many files to an archive. -#[must_use = "must call build() to finish building the archive"] -pub(crate) struct LlvmArchiveBuilder<'a> { - sess: &'a Session, - additions: Vec, -} - -enum Addition { - File { path: PathBuf, name_in_archive: String }, - Archive { path: PathBuf, archive: ArchiveRO, skip: Box bool> }, -} - -impl Addition { - fn path(&self) -> &Path { - match self { - Addition::File { path, .. } | Addition::Archive { path, .. } => path, - } - } -} - -fn is_relevant_child(c: &Child<'_>) -> bool { - match c.name() { - Some(name) => !name.contains("SYMDEF"), - None => false, - } -} - -impl<'a> ArchiveBuilder for LlvmArchiveBuilder<'a> { - fn add_archive( - &mut self, - archive: &Path, - skip: Box bool + 'static>, - ) -> io::Result<()> { - let mut archive = archive.to_path_buf(); - if self.sess.target.llvm_target.contains("-apple-macosx") { - if let Some(new_archive) = try_extract_macho_fat_archive(self.sess, &archive)? { - archive = new_archive - } - } - let archive_ro = match ArchiveRO::open(&archive) { - Ok(ar) => ar, - Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), - }; - if self.additions.iter().any(|ar| ar.path() == archive) { - return Ok(()); - } - self.additions.push(Addition::Archive { - path: archive, - archive: archive_ro, - skip: Box::new(skip), - }); - Ok(()) - } - - /// Adds an arbitrary file to this archive - fn add_file(&mut self, file: &Path) { - let name = file.file_name().unwrap().to_str().unwrap(); - self.additions - .push(Addition::File { path: file.to_path_buf(), name_in_archive: name.to_owned() }); - } - - /// Combine the provided files, rlibs, and native libraries into a single - /// `Archive`. - fn build(mut self: Box, output: &Path) -> bool { - match self.build_with_llvm(output) { - Ok(any_members) => any_members, - Err(error) => { - self.sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error }) - } - } - } -} +use crate::llvm; pub(crate) struct LlvmArchiveBuilderBuilder; impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box { - // Keeping LlvmArchiveBuilder around in case of a regression caused by using - // ArArchiveBuilder. - // FIXME(#128955) remove a couple of months after #128936 gets merged in case - // no regression is found. - if false { - Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() }) - } else { - Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER)) - } + // Use the `object` crate to build archives, with a little bit of help from LLVM. + Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER)) } } @@ -178,91 +95,3 @@ fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool { fn llvm_is_ec_object_file(buf: &[u8]) -> bool { unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) } } - -impl<'a> LlvmArchiveBuilder<'a> { - fn build_with_llvm(&mut self, output: &Path) -> io::Result { - let kind = &*self.sess.target.archive_format; - let kind = kind - .parse::() - .map_err(|_| kind) - .unwrap_or_else(|kind| self.sess.dcx().emit_fatal(UnknownArchiveKind { kind })); - - let mut additions = mem::take(&mut self.additions); - // Values in the `members` list below will contain pointers to the strings allocated here. - // So they need to get dropped after all elements of `members` get freed. - let mut strings = Vec::new(); - let mut members = Vec::new(); - - let dst = CString::new(output.to_str().unwrap())?; - - unsafe { - for addition in &mut additions { - match addition { - Addition::File { path, name_in_archive } => { - let path = CString::new(path.to_str().unwrap())?; - let name = CString::new(name_in_archive.as_bytes())?; - members.push(llvm::LLVMRustArchiveMemberNew( - path.as_ptr(), - name.as_ptr(), - None, - )); - strings.push(path); - strings.push(name); - } - Addition::Archive { archive, skip, .. } => { - for child in archive.iter() { - let child = child.map_err(string_to_io_error)?; - if !is_relevant_child(&child) { - continue; - } - let child_name = child.name().unwrap(); - if skip(child_name) { - continue; - } - - // It appears that LLVM's archive writer is a little - // buggy if the name we pass down isn't just the - // filename component, so chop that off here and - // pass it in. - // - // See LLVM bug 25877 for more info. - let child_name = - Path::new(child_name).file_name().unwrap().to_str().unwrap(); - let name = CString::new(child_name)?; - let m = llvm::LLVMRustArchiveMemberNew( - ptr::null(), - name.as_ptr(), - Some(child.raw), - ); - members.push(m); - strings.push(name); - } - } - } - } - - let r = llvm::LLVMRustWriteArchive( - dst.as_ptr(), - members.len() as libc::size_t, - members.as_ptr() as *const &_, - true, - kind, - self.sess.target.arch == "arm64ec", - ); - let ret = if r.into_result().is_err() { - let msg = last_error().unwrap_or_else(|| "failed to write archive".into()); - Err(io::Error::new(io::ErrorKind::Other, msg)) - } else { - Ok(!members.is_empty()) - }; - for member in members { - llvm::LLVMRustArchiveMemberFree(member); - } - ret - } - } -} - -fn string_to_io_error(s: String) -> io::Error { - io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}")) -} diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs new file mode 100644 index 000000000000..6cb89f80ab89 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod archive; +pub(crate) mod lto; +pub(crate) mod owned_target_machine; +mod profiling; +pub(crate) mod write; diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 8e82013e94ad..6d8178320feb 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -1,4 +1,5 @@ -use std::ffi::{CStr, c_char}; +use std::assert_matches::assert_matches; +use std::ffi::CStr; use std::marker::PhantomData; use std::ptr::NonNull; @@ -41,11 +42,9 @@ pub(crate) fn new( args_cstr_buff: &[u8], use_wasm_eh: bool, ) -> Result> { - assert!(args_cstr_buff.len() > 0); - assert!( - *args_cstr_buff.last().unwrap() == 0, - "The last character must be a null terminator." - ); + // The argument list is passed as the concatenation of one or more C strings. + // This implies that there must be a last byte, and it must be 0. + assert_matches!(args_cstr_buff, [.., b'\0'], "the last byte must be a NUL terminator"); // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data let tm_ptr = unsafe { @@ -71,7 +70,7 @@ pub(crate) fn new( output_obj_file.as_ptr(), debug_info_compression.as_ptr(), use_emulated_tls, - args_cstr_buff.as_ptr() as *const c_char, + args_cstr_buff.as_ptr(), args_cstr_buff.len(), use_wasm_eh, ) @@ -99,7 +98,7 @@ fn drop(&mut self) { // llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no // double free or use after free. unsafe { - llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_mut()); + llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr()); } } } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index cb98df59c1b1..427c75d40e9c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1453,7 +1453,7 @@ fn tail_call( instance: Option>, ) { let call = self.call(llty, fn_attrs, Some(fn_abi), llfn, args, funclet, instance); - llvm::LLVMRustSetTailCallKind(call, llvm::TailCallKind::MustTail); + llvm::LLVMSetTailCallKind(call, llvm::TailCallKind::MustTail); match &fn_abi.ret.mode { PassMode::Ignore | PassMode::Indirect { .. } => self.ret_void(), diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index b0f3494ea68c..4a7de7d2e69e 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -377,6 +377,15 @@ pub(crate) unsafe fn create_module<'ll>( } } + if let Some(regparm_count) = sess.opts.unstable_opts.regparm { + llvm::add_module_flag_u32( + llmod, + llvm::ModuleFlagMergeBehavior::Error, + "NumRegisterParameters", + regparm_count, + ); + } + if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { if sess.target.arch == "aarch64" { llvm::add_module_flag_u32( @@ -462,6 +471,15 @@ pub(crate) unsafe fn create_module<'ll>( } } + if sess.opts.unstable_opts.indirect_branch_cs_prefix { + llvm::add_module_flag_u32( + llmod, + llvm::ModuleFlagMergeBehavior::Override, + "indirect_branch_cs_prefix", + 1, + ); + } + match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support()) { // Set up the small-data optimization limit for architectures that use diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index d1502d2b1e62..18a783a348a4 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -276,7 +276,7 @@ pub(super) fn build_type_with_children<'ll, 'tcx>( && let ty::Adt(adt_def, args) = ty.kind() { let def_id = adt_def.did(); - // If any sub type reference the original type definition and the sub type has a type + // If any child type references the original type definition and the child type has a type // parameter that strictly contains the original parameter, the original type is a recursive // type that can expanding indefinitely. Example, // ``` @@ -285,21 +285,43 @@ pub(super) fn build_type_with_children<'ll, 'tcx>( // Item(T), // } // ``` - let is_expanding_recursive = adt_def.is_enum() - && debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { - if def_id == *parent_def_id { - args.iter().zip(parent_args.iter()).any(|(arg, parent_arg)| { - if let (Some(arg), Some(parent_arg)) = (arg.as_type(), parent_arg.as_type()) - { - arg != parent_arg && arg.contains(parent_arg) - } else { - false - } - }) - } else { - false - } - }); + let is_expanding_recursive = { + let stack = debug_context(cx).adt_stack.borrow(); + stack + .iter() + .enumerate() + .rev() + .skip(1) + .filter(|(_, (ancestor_def_id, _))| def_id == *ancestor_def_id) + .any(|(ancestor_index, (_, ancestor_args))| { + args.iter() + .zip(ancestor_args.iter()) + .filter_map(|(arg, ancestor_arg)| arg.as_type().zip(ancestor_arg.as_type())) + .any(|(arg, ancestor_arg)| + // Strictly contains. + (arg != ancestor_arg && arg.contains(ancestor_arg)) + // Check all types between current and ancestor use the + // ancestor_arg. + // Otherwise, duplicate wrappers in normal recursive type may be + // regarded as expanding. + // ``` + // struct Recursive { + // a: Box>, + // } + // ``` + // It can produce an ADT stack like this, + // - Box + // - Recursive + // - Box> + && stack[ancestor_index + 1..stack.len()].iter().all( + |(_, intermediate_args)| + intermediate_args + .iter() + .filter_map(|arg| arg.as_type()) + .any(|mid_arg| mid_arg.contains(ancestor_arg)) + )) + }) + }; if is_expanding_recursive { // FIXME: indicate that this is an expanding recursive type in stub metadata? return DINodeCreationResult::new(stub_info.metadata, false); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 4935f8d7dffd..49d3dedbeabd 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -330,10 +330,16 @@ fn codegen_intrinsic_call( _ => bug!(), }; let ptr = args[0].immediate(); + let locality = fn_args.const_at(1).to_value().valtree.unwrap_leaf().to_i32(); self.call_intrinsic( "llvm.prefetch", &[self.val_ty(ptr)], - &[ptr, self.const_i32(rw), args[1].immediate(), self.const_i32(cache_type)], + &[ + ptr, + self.const_i32(rw), + self.const_i32(locality), + self.const_i32(cache_type), + ], ) } sym::carrying_mul_add => { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 79e80db6f555..0fcf31d79930 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -46,18 +46,11 @@ use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_span::Symbol; -mod back { - pub(crate) mod archive; - pub(crate) mod lto; - pub(crate) mod owned_target_machine; - mod profiling; - pub(crate) mod write; -} - mod abi; mod allocator; mod asm; mod attributes; +mod back; mod base; mod builder; mod callee; diff --git a/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs b/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs deleted file mode 100644 index 51bcc4d123d3..000000000000 --- a/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! A wrapper around LLVM's archive (.a) code - -use std::path::Path; -use std::{slice, str}; - -use rustc_fs_util::path_to_c_string; - -pub(crate) struct ArchiveRO { - pub raw: &'static mut super::Archive, -} - -unsafe impl Send for ArchiveRO {} - -pub(crate) struct Iter<'a> { - raw: &'a mut super::ArchiveIterator<'a>, -} - -pub(crate) struct Child<'a> { - pub raw: &'a mut super::ArchiveChild<'a>, -} - -impl ArchiveRO { - /// Opens a static archive for read-only purposes. This is more optimized - /// than the `open` method because it uses LLVM's internal `Archive` class - /// rather than shelling out to `ar` for everything. - /// - /// If this archive is used with a mutable method, then an error will be - /// raised. - pub(crate) fn open(dst: &Path) -> Result { - unsafe { - let s = path_to_c_string(dst); - let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| { - super::last_error().unwrap_or_else(|| "failed to open archive".to_owned()) - })?; - Ok(ArchiveRO { raw: ar }) - } - } - - pub(crate) fn iter(&self) -> Iter<'_> { - unsafe { Iter { raw: super::LLVMRustArchiveIteratorNew(self.raw) } } - } -} - -impl Drop for ArchiveRO { - fn drop(&mut self) { - unsafe { - super::LLVMRustDestroyArchive(&mut *(self.raw as *mut _)); - } - } -} - -impl<'a> Iterator for Iter<'a> { - type Item = Result, String>; - - fn next(&mut self) -> Option, String>> { - unsafe { - match super::LLVMRustArchiveIteratorNext(self.raw) { - Some(raw) => Some(Ok(Child { raw })), - None => super::last_error().map(Err), - } - } - } -} - -impl<'a> Drop for Iter<'a> { - fn drop(&mut self) { - unsafe { - super::LLVMRustArchiveIteratorFree(&mut *(self.raw as *mut _)); - } - } -} - -impl<'a> Child<'a> { - pub(crate) fn name(&self) -> Option<&'a str> { - unsafe { - let mut name_len = 0; - let name_ptr = super::LLVMRustArchiveChildName(self.raw, &mut name_len); - if name_ptr.is_null() { - None - } else { - let name = slice::from_raw_parts(name_ptr as *const u8, name_len as usize); - str::from_utf8(name).ok().map(|s| s.trim()) - } - } - } -} - -impl<'a> Drop for Child<'a> { - fn drop(&mut self) { - unsafe { - super::LLVMRustArchiveChildFree(&mut *(self.raw as *mut _)); - } - } -} diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index ad3c3d5932ee..2461f70a86e3 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -97,6 +97,7 @@ pub(crate) enum ModuleFlagMergeBehavior { // Consts for the LLVM CallConv type, pre-cast to usize. +/// Must match the layout of `LLVMTailCallKind`. #[derive(Copy, Clone, PartialEq, Debug)] #[repr(C)] #[allow(dead_code)] @@ -250,6 +251,7 @@ pub(crate) enum AttributeKind { Writable = 42, DeadOnUnwind = 43, DeadOnReturn = 44, + CapturesReadOnly = 45, } /// LLVMIntPredicate @@ -332,10 +334,15 @@ pub(crate) fn from_generic(realp: rustc_codegen_ssa::common::RealPredicate) -> S } } -/// LLVMTypeKind -#[derive(Copy, Clone, PartialEq, Debug)] +/// Must match the layout of `LLVMTypeKind`. +/// +/// Use [`RawEnum`] for values of `LLVMTypeKind` returned from LLVM, +/// to avoid risk of UB if LLVM adds new enum values. +/// +/// All of LLVM's variants should be declared here, even if no Rust-side code refers +/// to them, because unknown variants will cause [`RawEnum::to_rust`] to panic. +#[derive(Copy, Clone, PartialEq, Debug, TryFromU32)] #[repr(C)] -#[expect(dead_code, reason = "Some variants are unused, but are kept to match LLVM-C")] pub(crate) enum TypeKind { Void = 0, Half = 1, @@ -610,17 +617,6 @@ pub(crate) enum DiagnosticLevel { Remark, } -/// LLVMRustArchiveKind -#[derive(Copy, Clone)] -#[repr(C)] -pub(crate) enum ArchiveKind { - K_GNU, - K_BSD, - K_DARWIN, - K_COFF, - K_AIXBIG, -} - unsafe extern "C" { // LLVMRustThinLTOData pub(crate) type ThinLTOData; @@ -769,19 +765,12 @@ struct InvariantOpaque<'a> { pub(crate) struct PassManager<'a>(InvariantOpaque<'a>); unsafe extern "C" { pub type TargetMachine; - pub(crate) type Archive; } -#[repr(C)] -pub(crate) struct ArchiveIterator<'a>(InvariantOpaque<'a>); -#[repr(C)] -pub(crate) struct ArchiveChild<'a>(InvariantOpaque<'a>); unsafe extern "C" { pub(crate) type Twine; pub(crate) type DiagnosticInfo; pub(crate) type SMDiagnostic; } -#[repr(C)] -pub(crate) struct RustArchiveMember<'a>(InvariantOpaque<'a>); /// Opaque pointee of `LLVMOperandBundleRef`. #[repr(C)] pub(crate) struct OperandBundle<'a>(InvariantOpaque<'a>); @@ -1046,6 +1035,8 @@ pub(crate) fn LLVMGetInlineAsm<'ll>( CanThrow: llvm::Bool, ) -> &'ll Value; + pub(crate) safe fn LLVMGetTypeKind(Ty: &Type) -> RawEnum; + // Operations on integer types pub(crate) fn LLVMInt1TypeInContext(C: &Context) -> &Type; pub(crate) fn LLVMInt8TypeInContext(C: &Context) -> &Type; @@ -1197,7 +1188,7 @@ pub(crate) fn LLVMConstInBoundsGEP2<'a>( pub(crate) safe fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool; pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); - pub(crate) safe fn LLVMRustSetTailCallKind(CallInst: &Value, Kind: TailCallKind); + pub(crate) safe fn LLVMSetTailCallKind(CallInst: &Value, kind: TailCallKind); // Operations on attributes pub(crate) fn LLVMCreateStringAttribute( @@ -1841,9 +1832,6 @@ pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>( // Create and destroy contexts. pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; - /// See llvm::LLVMTypeKind::getTypeID. - pub(crate) fn LLVMRustGetTypeKind(Ty: &Type) -> TypeKind; - // Operations on all values pub(crate) fn LLVMRustGlobalAddMetadata<'a>( Val: &'a Value, @@ -2438,7 +2426,7 @@ pub(crate) fn LLVMRustCreateTargetMachine( OutputObjFile: *const c_char, DebugInfoCompression: *const c_char, UseEmulatedTls: bool, - ArgsCstrBuff: *const c_char, + ArgsCstrBuff: *const c_uchar, // See "PTR_LEN_STR". ArgsCstrBuffLen: usize, UseWasmEH: bool, ) -> *mut TargetMachine; @@ -2505,19 +2493,6 @@ pub(crate) fn LLVMRustPrintModule( pub(crate) fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char); pub(crate) fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); - pub(crate) fn LLVMRustOpenArchive(path: *const c_char) -> Option<&'static mut Archive>; - pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; - pub(crate) fn LLVMRustArchiveIteratorNext<'a>( - AIR: &ArchiveIterator<'a>, - ) -> Option<&'a mut ArchiveChild<'a>>; - pub(crate) fn LLVMRustArchiveChildName( - ACR: &ArchiveChild<'_>, - size: &mut size_t, - ) -> *const c_char; - pub(crate) fn LLVMRustArchiveChildFree<'a>(ACR: &'a mut ArchiveChild<'a>); - pub(crate) fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>); - pub(crate) fn LLVMRustDestroyArchive(AR: &'static mut Archive); - pub(crate) fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString); pub(crate) fn LLVMRustUnpackOptimizationDiagnostic<'a>( @@ -2555,21 +2530,6 @@ pub(crate) fn LLVMRustUnpackSMDiagnostic( num_ranges: &mut usize, ) -> bool; - pub(crate) fn LLVMRustWriteArchive( - Dst: *const c_char, - NumMembers: size_t, - Members: *const &RustArchiveMember<'_>, - WriteSymbtab: bool, - Kind: ArchiveKind, - isEC: bool, - ) -> LLVMRustResult; - pub(crate) fn LLVMRustArchiveMemberNew<'a>( - Filename: *const c_char, - Name: *const c_char, - Child: Option<&ArchiveChild<'a>>, - ) -> &'a mut RustArchiveMember<'a>; - pub(crate) fn LLVMRustArchiveMemberFree<'a>(Member: &'a mut RustArchiveMember<'a>); - pub(crate) fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine); pub(crate) fn LLVMRustPositionBuilderPastAllocas<'a>(B: &Builder<'a>, Fn: &'a Value); diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 0ea0af0c9afb..7fea7b79a8cf 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -3,7 +3,6 @@ use std::ffi::{CStr, CString}; use std::num::NonZero; use std::ptr; -use std::str::FromStr; use std::string::FromUtf8Error; use libc::c_uint; @@ -16,7 +15,6 @@ pub(crate) use self::ffi::*; use crate::common::AsCCharPtr; -pub(crate) mod archive_ro; pub(crate) mod diagnostic; pub(crate) mod enzyme_ffi; mod ffi; @@ -152,21 +150,6 @@ pub(crate) enum CodeGenOptSize { CodeGenOptSizeAggressive = 2, } -impl FromStr for ArchiveKind { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "gnu" => Ok(ArchiveKind::K_GNU), - "bsd" => Ok(ArchiveKind::K_BSD), - "darwin" => Ok(ArchiveKind::K_DARWIN), - "coff" => Ok(ArchiveKind::K_COFF), - "aix_big" => Ok(ArchiveKind::K_AIXBIG), - _ => Err(()), - } - } -} - pub(crate) fn SetInstructionCallConv(instr: &Value, cc: CallConv) { unsafe { LLVMSetInstructionCallConv(instr, cc as c_uint); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 28d2100f478c..90f7cd43268f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -277,6 +277,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option None, // Filter out features that are not supported by the current LLVM version ("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None, ( diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 893655031388..f02d16baf94e 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -204,7 +204,7 @@ fn type_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { } fn type_kind(&self, ty: &'ll Type) -> TypeKind { - unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() } + llvm::LLVMGetTypeKind(ty).to_rust().to_generic() } fn type_ptr(&self) -> &'ll Type { diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index b6cfea883638..44b9941691a6 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -171,9 +171,6 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol = invalid monomorphizati codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` of size `{$size}` to `{$ret_ty}` -codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize` - .note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` - codegen_ssa_invalid_windows_subsystem = invalid windows subsystem `{$subsystem}`, only `windows` and `console` are allowed codegen_ssa_ld64_unimplemented_modifier = `as-needed` modifier not implemented yet for ld64 diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 4ebe59dc2a79..c3777f64e9e9 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2435,6 +2435,13 @@ fn linker_with_args( // Passed after compiler-generated options to support manual overriding when necessary. add_user_defined_link_args(cmd, sess); + // ------------ Builtin configurable linker scripts ------------ + // The user's link args should be able to overwrite symbols in the compiler's + // linker script that were weakly defined (i.e. defined with `PROVIDE()`). For this + // to work correctly, the user needs to be able to specify linker arguments like + // `--defsym` and `--script` *before* any builtin linker scripts are evaluated. + add_link_script(cmd, sess, tmpdir, crate_type); + // ------------ Object code and libraries, order-dependent ------------ // Post-link CRT objects. @@ -2469,8 +2476,6 @@ fn add_order_independent_options( let apple_sdk_root = add_apple_sdk(cmd, sess, flavor); - add_link_script(cmd, sess, tmpdir, crate_type); - if sess.target.os == "fuchsia" && crate_type == CrateType::Executable && !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 67cd1f4cd41a..8abaf201abae 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -858,7 +858,7 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( instance: Instance<'tcx>, ) -> bool { fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name { + if let Some(name) = tcx.codegen_fn_attrs(def_id).symbol_name { name.as_str().starts_with("llvm.") } else { false diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index af70f0deb070..6b0bd64102c6 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -6,7 +6,6 @@ use rustc_hir::attrs::{AttributeKind, InlineAttr, InstructionSetAttr, UsedBy}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; -use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; use rustc_hir::{self as hir, Attribute, LangItem, find_attr, lang_items}; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, @@ -77,32 +76,6 @@ fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option, attr: &Attribute) -> Option { - let list = attr.meta_item_list()?; - let mut sanitizer_set = SanitizerSet::empty(); - - for item in list.iter() { - match item.name() { - Some(sym::address) => { - sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS - } - Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI, - Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI, - Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY, - Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG, - Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK, - Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD, - Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS, - _ => { - tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() }); - } - } - } - - Some(sanitizer_set) -} - // FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr fn parse_patchable_function_entry( tcx: TyCtxt<'_>, @@ -161,7 +134,7 @@ fn parse_patchable_function_entry( #[derive(Default)] struct InterestingAttributeDiagnosticSpans { link_ordinal: Option, - no_sanitize: Option, + sanitize: Option, inline: Option, no_mangle: Option, } @@ -182,7 +155,7 @@ fn process_builtin_attrs( match p { AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, AttributeKind::ExportName { name, .. } => { - codegen_fn_attrs.export_name = Some(*name) + codegen_fn_attrs.symbol_name = Some(*name) } AttributeKind::Inline(inline, span) => { codegen_fn_attrs.inline = *inline; @@ -190,7 +163,13 @@ fn process_builtin_attrs( } AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), - AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name), + AttributeKind::LinkName { name, .. } => { + // FIXME Remove check for foreign functions once #[link_name] on non-foreign + // functions is a hard error + if tcx.is_foreign_item(did) { + codegen_fn_attrs.symbol_name = Some(*name); + } + } AttributeKind::LinkOrdinal { ordinal, span } => { codegen_fn_attrs.link_ordinal = Some(*ordinal); interesting_spans.link_ordinal = Some(*span); @@ -214,7 +193,7 @@ fn process_builtin_attrs( } } AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize, - AttributeKind::TargetFeature(features, attr_span) => { + AttributeKind::TargetFeature { features, attr_span, was_forced } => { let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn"); continue; @@ -222,7 +201,7 @@ fn process_builtin_attrs( let safe_target_features = matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures); codegen_fn_attrs.safe_target_features = safe_target_features; - if safe_target_features { + if safe_target_features && !was_forced { if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc { // The `#[target_feature]` attribute is allowed on // WebAssembly targets on all functions. Prior to stabilizing @@ -253,6 +232,7 @@ fn process_builtin_attrs( tcx, did, features, + *was_forced, rust_target_features, &mut codegen_fn_attrs.target_features, ); @@ -313,6 +293,9 @@ fn process_builtin_attrs( codegen_fn_attrs.linkage = linkage; } } + AttributeKind::Sanitize { span, .. } => { + interesting_spans.sanitize = Some(*span); + } _ => {} } } @@ -330,11 +313,6 @@ fn process_builtin_attrs( codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, - sym::no_sanitize => { - interesting_spans.no_sanitize = Some(attr.span()); - codegen_fn_attrs.no_sanitize |= - parse_no_sanitize_attr(tcx, attr).unwrap_or_default(); - } sym::instruction_set => { codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr) } @@ -358,6 +336,8 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment); + // Compute the disabled sanitizers. + codegen_fn_attrs.no_sanitize |= tcx.disabled_sanitizers_for(did); // On trait methods, inherit the `#[align]` of the trait's method prototype. codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did)); @@ -410,7 +390,7 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code // * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way // both for exports and imports through foreign items. This is handled further, // during symbol mangling logic. - } else if codegen_fn_attrs.link_name.is_some() { + } else if codegen_fn_attrs.symbol_name.is_some() { // * This can be overridden with the `#[link_name]` attribute } else { // NOTE: there's one more exception that we cannot apply here. On wasm, @@ -455,17 +435,17 @@ fn check_result( if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline.always() && let (Some(no_sanitize_span), Some(inline_span)) = - (interesting_spans.no_sanitize, interesting_spans.inline) + (interesting_spans.sanitize, interesting_spans.inline) { let hir_id = tcx.local_def_id_to_hir_id(did); tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| { - lint.primary_message("`no_sanitize` will have no effect after inlining"); + lint.primary_message("setting `sanitize` off will have no effect after inlining"); lint.span_note(inline_span, "inlining requested here"); }) } // error when specifying link_name together with link_ordinal - if let Some(_) = codegen_fn_attrs.link_name + if let Some(_) = codegen_fn_attrs.symbol_name && let Some(_) = codegen_fn_attrs.link_ordinal { let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; @@ -485,7 +465,7 @@ fn check_result( .collect(), ) { let span = - find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature(_, span) => *span) + find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature{attr_span: span, ..} => *span) .unwrap_or_else(|| tcx.def_span(did)); tcx.dcx() @@ -512,14 +492,11 @@ fn handle_lang_items( // strippable by the linker. // // Additionally weak lang items have predetermined symbol names. - if let Some(lang_item) = lang_item { - if WEAK_LANG_ITEMS.contains(&lang_item) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; - } - if let Some(link_name) = lang_item.link_name() { - codegen_fn_attrs.export_name = Some(link_name); - codegen_fn_attrs.link_name = Some(link_name); - } + if let Some(lang_item) = lang_item + && let Some(link_name) = lang_item.link_name() + { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + codegen_fn_attrs.symbol_name = Some(link_name); } // error when using no_mangle on a lang item item @@ -585,6 +562,32 @@ fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { } } +fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { + // Backtrack to the crate root. + let mut disabled = match tcx.opt_local_parent(did) { + // Check the parent (recursively). + Some(parent) => tcx.disabled_sanitizers_for(parent), + // We reached the crate root without seeing an attribute, so + // there is no sanitizers to exclude. + None => SanitizerSet::empty(), + }; + + // Check for a sanitize annotation directly on this def. + if let Some((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did), AttributeKind::Sanitize {on_set, off_set, ..} => (on_set, off_set)) + { + // the on set is the set of sanitizers explicitly enabled. + // we mask those out since we want the set of disabled sanitizers here + disabled &= !*on_set; + // the off set is the set of sanitizers explicitly disabled. + // we or those in here. + disabled |= *off_set; + // the on set and off set are distjoint since there's a third option: unset. + // a node may not set the sanitizer setting in which case it inherits from parents. + // the code above in this function does this backtracking + } + disabled +} + /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { @@ -709,6 +712,11 @@ pub fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { } pub(crate) fn provide(providers: &mut Providers) { - *providers = - Providers { codegen_fn_attrs, should_inherit_track_caller, inherited_align, ..*providers }; + *providers = Providers { + codegen_fn_attrs, + should_inherit_track_caller, + inherited_align, + disabled_sanitizers_for, + ..*providers + }; } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 7ac830bcda91..fb5a82051405 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1120,14 +1120,6 @@ fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { } } -#[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_no_sanitize)] -#[note] -pub(crate) struct InvalidNoSanitize { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_target_feature_safe_trait)] pub(crate) struct TargetFeatureSafeTrait { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 7e4341a82366..54584999d61b 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -3,7 +3,7 @@ use rustc_hir::attrs::InstructionSetAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; -use rustc_middle::middle::codegen_fn_attrs::TargetFeature; +use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -22,6 +22,7 @@ pub(crate) fn from_target_feature_attr( tcx: TyCtxt<'_>, did: LocalDefId, features: &[(Symbol, Span)], + was_forced: bool, rust_target_features: &UnordMap, target_features: &mut Vec, ) { @@ -88,7 +89,14 @@ pub(crate) fn from_target_feature_attr( } } } - target_features.push(TargetFeature { name, implied: name != feature }) + let kind = if name != feature { + TargetFeatureKind::Implied + } else if was_forced { + TargetFeatureKind::Forced + } else { + TargetFeatureKind::Enabled + }; + target_features.push(TargetFeature { name, kind }) } } } @@ -180,6 +188,7 @@ fn parse_rust_feature_flag<'a>( while let Some(new_feature) = new_features.pop() { if features.insert(new_feature) { if let Some(implied_features) = inverse_implied_features.get(&new_feature) { + #[allow(rustc::potential_query_instability)] new_features.extend(implied_features) } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 64cb934ac8d0..4cb88d44e1b1 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -27,8 +27,9 @@ pub enum FnArg<'tcx, Prov: Provenance = CtfeProvenance> { /// Pass a copy of the given operand. Copy(OpTy<'tcx, Prov>), - /// Allow for the argument to be passed in-place: destroy the value originally stored at that place and - /// make the place inaccessible for the duration of the function call. + /// Allow for the argument to be passed in-place: destroy the value originally stored at that + /// place and make the place inaccessible for the duration of the function call. This *must* be + /// an in-memory place so that we can do the proper alias checks. InPlace(MPlaceTy<'tcx, Prov>), } @@ -379,6 +380,11 @@ pub fn init_stack_frame( } } + // *Before* pushing the new frame, determine whether the return destination is in memory. + // Need to use `place_to_op` to be *sure* we get the mplace if there is one. + let destination_mplace = self.place_to_op(destination)?.as_mplace_or_imm().left(); + + // Push the "raw" frame -- this leaves locals uninitialized. self.push_stack_frame_raw(instance, body, destination, cont)?; // If an error is raised here, pop the frame again to get an accurate backtrace. @@ -496,7 +502,7 @@ pub fn init_stack_frame( // Protect return place for in-place return value passing. // We only need to protect anything if this is actually an in-memory place. - if let Left(mplace) = destination.as_mplace_or_local() { + if let Some(mplace) = destination_mplace { M::protect_in_place_function_argument(self, &mplace)?; } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index a8a1ac1c9809..9681d89ce35f 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -325,8 +325,7 @@ pub fn instantiate_from_frame_and_normalize_erasing_regions>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + let _trace = enter_trace_span!(M, const_eval::eval_mir_constant, ?val); let const_val = val.eval(*self.tcx, self.typing_env, span).map_err(|err| { if M::ALL_CONSTS_ARE_PRECHECKED { match err { diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 53a440b646b0..560b0e1ae4e6 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -175,6 +175,16 @@ pub fn clear_provenance<'tcx>(&mut self) -> InterpResult<'tcx> { } interp_ok(()) } + + pub fn has_provenance(&self) -> bool { + match self { + Immediate::Scalar(scalar) => matches!(scalar, Scalar::Ptr { .. }), + Immediate::ScalarPair(s1, s2) => { + matches!(s1, Scalar::Ptr { .. }) || matches!(s2, Scalar::Ptr { .. }) + } + Immediate::Uninit => false, + } + } } // ScalarPair needs a type to interpret, so we often have an immediate and a type together diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 3255ffa54aaa..a86fdf80f601 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -234,6 +234,12 @@ pub(super) fn place(&self) -> &Place { } /// A place is either an mplace or some local. + /// + /// Note that the return value can be different even for logically identical places! + /// Specifically, if a local is stored in-memory, this may return `Local` or `MPlaceTy` + /// depending on how the place was constructed. In other words, seeing `Local` here does *not* + /// imply that this place does not point to memory. Every caller must therefore always handle + /// both cases. #[inline(always)] pub fn as_mplace_or_local( &self, @@ -759,6 +765,13 @@ pub fn clear_provenance( &mut self, dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { + // If this is an efficiently represented local variable without provenance, skip the + // `as_mplace_or_mutable_local` that would otherwise force this local into memory. + if let Right(imm) = dest.to_op(self)?.as_mplace_or_imm() { + if !imm.has_provenance() { + return interp_ok(()); + } + } match self.as_mplace_or_mutable_local(&dest.to_place())? { Right((local_val, _local_layout, local)) => { local_val.clear_provenance()?; diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 73cc87508ef9..7cabfd961212 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -20,7 +20,7 @@ MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout, interp_ok, throw_ub, throw_unsup, }; -use crate::errors; +use crate::{enter_trace_span, errors}; // The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread // boundary and dropped in the other thread, it would exit the span in the other thread. @@ -386,6 +386,9 @@ pub(crate) fn push_stack_frame_raw( // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). for &const_ in body.required_consts() { + // We can't use `eval_mir_constant` here as that assumes that all required consts have + // already been checked, so we need a separate tracing call. + let _trace = enter_trace_span!(M, const_eval::required_consts, ?const_.const_); let c = self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?; c.eval(*self.tcx, self.typing_env, const_.span).map_err(|err| { diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index f1995b3f132f..23d362de308f 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -2,8 +2,11 @@ //! //! The main entry point is the `step` method. +use std::iter; + use either::Either; use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexSlice; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, mir, span_bug}; @@ -389,8 +392,9 @@ fn write_repeat( /// Evaluate the arguments of a function call fn eval_fn_call_argument( - &self, + &mut self, op: &mir::Operand<'tcx>, + move_definitely_disjoint: bool, ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { interp_ok(match op { mir::Operand::Copy(_) | mir::Operand::Constant(_) => { @@ -399,24 +403,19 @@ fn eval_fn_call_argument( FnArg::Copy(op) } mir::Operand::Move(place) => { - // If this place lives in memory, preserve its location. - // We call `place_to_op` which will be an `MPlaceTy` whenever there exists - // an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local` - // which can return a local even if that has an mplace.) let place = self.eval_place(*place)?; - let op = self.place_to_op(&place)?; - - match op.as_mplace_or_imm() { - Either::Left(mplace) => FnArg::InPlace(mplace), - Either::Right(_imm) => { - // This argument doesn't live in memory, so there's no place - // to make inaccessible during the call. - // We rely on there not being any stray `PlaceTy` that would let the - // caller directly access this local! - // This is also crucial for tail calls, where we want the `FnArg` to - // stay valid when the old stack frame gets popped. - FnArg::Copy(op) + if move_definitely_disjoint { + // We still have to ensure that no *other* pointers are used to access this place, + // so *if* it is in memory then we have to treat it as `InPlace`. + // Use `place_to_op` to guarantee that we notice it being in memory. + let op = self.place_to_op(&place)?; + match op.as_mplace_or_imm() { + Either::Left(mplace) => FnArg::InPlace(mplace), + Either::Right(_imm) => FnArg::Copy(op), } + } else { + // We have to force this into memory to detect aliasing among `Move` arguments. + FnArg::InPlace(self.force_allocation(&place)?) } } }) @@ -425,18 +424,53 @@ fn eval_fn_call_argument( /// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the /// necessary information about callee and arguments to make a call. fn eval_callee_and_args( - &self, + &mut self, terminator: &mir::Terminator<'tcx>, func: &mir::Operand<'tcx>, args: &[Spanned>], + dest: &mir::Place<'tcx>, ) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> { let func = self.eval_operand(func, None)?; + + // Evaluating function call arguments. The tricky part here is dealing with `Move` + // arguments: we have to ensure no two such arguments alias. This would be most easily done + // by just forcing them all into memory and then doing the usual in-place argument + // protection, but then we'd force *a lot* of arguments into memory. So we do some syntactic + // pre-processing here where if all `move` arguments are syntactically distinct local + // variables (and none is indirect), we can skip the in-memory forcing. + // We have to include `dest` in that list so that we can detect aliasing of an in-place + // argument with the return place. + let move_definitely_disjoint = 'move_definitely_disjoint: { + let mut previous_locals = FxHashSet::::default(); + for place in args + .iter() + .filter_map(|a| { + // We only have to care about `Move` arguments. + if let mir::Operand::Move(place) = &a.node { Some(place) } else { None } + }) + .chain(iter::once(dest)) + { + if place.is_indirect_first_projection() { + // An indirect in-place argument could alias with anything else... + break 'move_definitely_disjoint false; + } + if !previous_locals.insert(place.local) { + // This local is the base for two arguments! They might overlap. + break 'move_definitely_disjoint false; + } + } + // We found no violation so they are all definitely disjoint. + true + }; let args = args .iter() - .map(|arg| self.eval_fn_call_argument(&arg.node)) + .map(|arg| self.eval_fn_call_argument(&arg.node, move_definitely_disjoint)) .collect::>>()?; - let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); + let fn_sig_binder = { + let _trace = enter_trace_span!(M, "fn_sig", ty = ?func.layout.ty.kind()); + func.layout.ty.fn_sig(*self.tcx) + }; let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, fn_sig_binder); let extra_args = &args[fn_sig.inputs().len()..]; let extra_args = @@ -519,7 +553,7 @@ fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResul let old_loc = self.frame().loc; let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = - self.eval_callee_and_args(terminator, func, args)?; + self.eval_callee_and_args(terminator, func, args, &destination)?; let destination = self.eval_place(destination)?; self.init_fn_call( @@ -542,7 +576,7 @@ fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResul let old_frame_idx = self.frame_idx(); let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = - self.eval_callee_and_args(terminator, func, args)?; + self.eval_callee_and_args(terminator, func, args, &mir::Place::return_place())?; self.init_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5e8bee65706b..02e3d90f4af7 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -1418,7 +1418,9 @@ pub fn validate_operand( let _trace = enter_trace_span!( M, "validate_operand", - "recursive={recursive}, reset_provenance_and_padding={reset_provenance_and_padding}, val={val:?}" + recursive, + reset_provenance_and_padding, + ?val, ); // Note that we *could* actually be in CTFE here with `-Zextra-const-ub-checks`, but it's diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 13cc607135a0..2ae6655901b6 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use rustc_data_structures::intern::Interned; -use rustc_hir::def_id::CrateNum; +use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::DisambiguatedDefPathData; use rustc_middle::bug; use rustc_middle::ty::print::{PrettyPrinter, PrintError, Printer}; @@ -132,15 +132,44 @@ fn print_path_with_generic_args( Ok(()) } } + + fn print_coroutine_with_kind( + &mut self, + def_id: DefId, + parent_args: &'tcx [GenericArg<'tcx>], + kind: Ty<'tcx>, + ) -> Result<(), PrintError> { + self.print_def_path(def_id, parent_args)?; + + let ty::Coroutine(_, args) = self.tcx.type_of(def_id).instantiate_identity().kind() else { + // Could be `ty::Error`. + return Ok(()); + }; + + let default_kind = args.as_coroutine().kind_ty(); + + match kind.to_opt_closure_kind() { + _ if kind == default_kind => { + // No need to mark the closure if it's the deduced coroutine kind. + } + Some(ty::ClosureKind::Fn) | None => { + // Should never happen. Just don't mark anything rather than panicking. + } + Some(ty::ClosureKind::FnMut) => self.path.push_str("::{{call_mut}}"), + Some(ty::ClosureKind::FnOnce) => self.path.push_str("::{{call_once}}"), + } + + Ok(()) + } } impl<'tcx> PrettyPrinter<'tcx> for TypeNamePrinter<'tcx> { - fn should_print_optional_region(&self, _region: ty::Region<'_>) -> bool { + fn should_print_optional_region(&self, region: ty::Region<'_>) -> bool { // Bound regions are always printed (as `'_`), which gives some idea that they are special, // even though the `for` is omitted by the pretty printer. // E.g. `for<'a, 'b> fn(&'a u32, &'b u32)` is printed as "fn(&'_ u32, &'_ u32)". - match _region.kind() { - ty::ReErased => false, + match region.kind() { + ty::ReErased | ty::ReEarlyParam(_) => false, ty::ReBound(..) => true, _ => unreachable!(), } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 53178d09348d..17da3ea83c81 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -77,6 +77,7 @@ pub mod thousands; pub mod transitive_relation; pub mod unhash; +pub mod union_find; pub mod unord; pub mod vec_cache; pub mod work_queue; diff --git a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs b/compiler/rustc_data_structures/src/union_find.rs similarity index 92% rename from compiler/rustc_mir_transform/src/coverage/counters/union_find.rs rename to compiler/rustc_data_structures/src/union_find.rs index a826a953fa67..ef73cd7ab40a 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs +++ b/compiler/rustc_data_structures/src/union_find.rs @@ -9,7 +9,7 @@ /// Simple implementation of a union-find data structure, i.e. a disjoint-set /// forest. #[derive(Debug)] -pub(crate) struct UnionFind { +pub struct UnionFind { table: IndexVec>, } @@ -28,7 +28,7 @@ struct UnionFindEntry { impl UnionFind { /// Creates a new disjoint-set forest containing the keys `0..num_keys`. /// Initially, every key is part of its own one-element set. - pub(crate) fn new(num_keys: usize) -> Self { + pub fn new(num_keys: usize) -> Self { // Initially, every key is the root of its own set, so its parent is itself. Self { table: IndexVec::from_fn_n(|key| UnionFindEntry { parent: key, rank: 0 }, num_keys) } } @@ -38,7 +38,7 @@ pub(crate) fn new(num_keys: usize) -> Self { /// /// Also updates internal data structures to make subsequent `find` /// operations faster. - pub(crate) fn find(&mut self, key: Key) -> Key { + pub fn find(&mut self, key: Key) -> Key { // Loop until we find a key that is its own parent. let mut curr = key; while let parent = self.table[curr].parent @@ -60,7 +60,7 @@ pub(crate) fn find(&mut self, key: Key) -> Key { /// Merges the set containing `a` and the set containing `b` into one set. /// /// Returns the common root of both keys, after the merge. - pub(crate) fn unify(&mut self, a: Key, b: Key) -> Key { + pub fn unify(&mut self, a: Key, b: Key) -> Key { let mut a = self.find(a); let mut b = self.find(b); @@ -90,7 +90,7 @@ pub(crate) fn unify(&mut self, a: Key, b: Key) -> Key { /// Takes a "snapshot" of the current state of this disjoint-set forest, in /// the form of a vector that directly maps each key to its current root. - pub(crate) fn snapshot(&mut self) -> IndexVec { + pub fn snapshot(&mut self) -> IndexVec { self.table.indices().map(|key| self.find(key)).collect() } } diff --git a/compiler/rustc_mir_transform/src/coverage/counters/union_find/tests.rs b/compiler/rustc_data_structures/src/union_find/tests.rs similarity index 100% rename from compiler/rustc_mir_transform/src/coverage/counters/union_find/tests.rs rename to compiler/rustc_data_structures/src/union_find/tests.rs diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index 0951859fa531..552ad672752d 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -11,6 +11,8 @@ icu_list = "1.2" icu_locid = "1.2" icu_provider_adapters = "1.2" intl-memoizer = "0.5.1" +rustc_ast = { path = "../rustc_ast" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_baked_icu_data = { path = "../rustc_baked_icu_data" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_error_messages/src/diagnostic_impls.rs b/compiler/rustc_error_messages/src/diagnostic_impls.rs new file mode 100644 index 000000000000..3b664cce5776 --- /dev/null +++ b/compiler/rustc_error_messages/src/diagnostic_impls.rs @@ -0,0 +1,205 @@ +use std::backtrace::Backtrace; +use std::borrow::Cow; +use std::fmt; +use std::num::ParseIntError; +use std::path::{Path, PathBuf}; +use std::process::ExitStatus; + +use rustc_ast as ast; +use rustc_ast_pretty::pprust; +use rustc_span::edition::Edition; + +use crate::{DiagArgValue, IntoDiagArg}; + +pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display); + +impl IntoDiagArg for DiagArgFromDisplay<'_> { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.0.to_string().into_diag_arg(path) + } +} + +impl<'a> From<&'a dyn fmt::Display> for DiagArgFromDisplay<'a> { + fn from(t: &'a dyn fmt::Display) -> Self { + DiagArgFromDisplay(t) + } +} + +impl<'a, T: fmt::Display> From<&'a T> for DiagArgFromDisplay<'a> { + fn from(t: &'a T) -> Self { + DiagArgFromDisplay(t) + } +} + +impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.clone().into_diag_arg(path) + } +} + +#[macro_export] +macro_rules! into_diag_arg_using_display { + ($( $ty:ty ),+ $(,)?) => { + $( + impl $crate::IntoDiagArg for $ty { + fn into_diag_arg(self, path: &mut Option) -> $crate::DiagArgValue { + self.to_string().into_diag_arg(path) + } + } + )+ + } +} + +macro_rules! into_diag_arg_for_number { + ($( $ty:ty ),+ $(,)?) => { + $( + impl $crate::IntoDiagArg for $ty { + fn into_diag_arg(self, path: &mut Option) -> $crate::DiagArgValue { + // Convert to a string if it won't fit into `Number`. + #[allow(irrefutable_let_patterns)] + if let Ok(n) = TryInto::::try_into(self) { + $crate::DiagArgValue::Number(n) + } else { + self.to_string().into_diag_arg(path) + } + } + } + )+ + } +} + +into_diag_arg_using_display!( + ast::ParamKindOrd, + std::io::Error, + Box, + std::num::NonZero, + Edition, + rustc_span::Ident, + rustc_span::MacroRulesNormalizedIdent, + ParseIntError, + ExitStatus, +); + +into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); + +impl IntoDiagArg for bool { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + if self { + DiagArgValue::Str(Cow::Borrowed("true")) + } else { + DiagArgValue::Str(Cow::Borrowed("false")) + } + } +} + +impl IntoDiagArg for char { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(format!("{self:?}"))) + } +} + +impl IntoDiagArg for Vec { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::StrListSepByAnd( + self.into_iter().map(|c| Cow::Owned(format!("{c:?}"))).collect(), + ) + } +} + +impl IntoDiagArg for rustc_span::Symbol { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.to_ident_string().into_diag_arg(path) + } +} + +impl<'a> IntoDiagArg for &'a str { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.to_string().into_diag_arg(path) + } +} + +impl IntoDiagArg for String { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self)) + } +} + +impl<'a> IntoDiagArg for Cow<'a, str> { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.into_owned())) + } +} + +impl<'a> IntoDiagArg for &'a Path { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.display().to_string())) + } +} + +impl IntoDiagArg for PathBuf { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.display().to_string())) + } +} + +impl IntoDiagArg for ast::Expr { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(pprust::expr_to_string(&self))) + } +} + +impl IntoDiagArg for ast::Path { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) + } +} + +impl IntoDiagArg for ast::token::Token { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(pprust::token_to_string(&self)) + } +} + +impl IntoDiagArg for ast::token::TokenKind { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(pprust::token_kind_to_string(&self)) + } +} + +impl IntoDiagArg for std::ffi::CString { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) + } +} + +impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) + } +} + +impl IntoDiagArg for ast::Visibility { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + let s = pprust::vis_to_string(&self); + let s = s.trim_end().to_string(); + DiagArgValue::Str(Cow::Owned(s)) + } +} + +impl IntoDiagArg for Backtrace { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::from(self.to_string())) + } +} + +impl IntoDiagArg for ast::util::parser::ExprPrecedence { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Number(self as i32) + } +} + +impl IntoDiagArg for ast::FloatTy { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.name_str())) + } +} diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 4b3ecad307fe..d8bacbe762b1 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -23,6 +23,9 @@ use tracing::{instrument, trace}; pub use unic_langid::{LanguageIdentifier, langid}; +mod diagnostic_impls; +pub use diagnostic_impls::DiagArgFromDisplay; + pub type FluentBundle = IntoDynSyncSend>; @@ -589,3 +592,53 @@ fn construct(lang: LanguageIdentifier, _args: Self::Args) -> Result = (&'iter DiagArgName, &'iter DiagArgValue); + +/// Name of a diagnostic argument. +pub type DiagArgName = Cow<'static, str>; + +/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted +/// to a `FluentValue` by the emitter to be used in diagnostic translation. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +pub enum DiagArgValue { + Str(Cow<'static, str>), + // This gets converted to a `FluentNumber`, which is an `f64`. An `i32` + // safely fits in an `f64`. Any integers bigger than that will be converted + // to strings in `into_diag_arg` and stored using the `Str` variant. + Number(i32), + StrListSepByAnd(Vec>), +} + +/// Converts a value of a type into a `DiagArg` (typically a field of an `Diag` struct). +/// Implemented as a custom trait rather than `From` so that it is implemented on the type being +/// converted rather than on `DiagArgValue`, which enables types from other `rustc_*` crates to +/// implement this. +pub trait IntoDiagArg { + /// Convert `Self` into a `DiagArgValue` suitable for rendering in a diagnostic. + /// + /// It takes a `path` where "long values" could be written to, if the `DiagArgValue` is too big + /// for displaying on the terminal. This path comes from the `Diag` itself. When rendering + /// values that come from `TyCtxt`, like `Ty<'_>`, they can use `TyCtxt::short_string`. If a + /// value has no shortening logic that could be used, the argument can be safely ignored. + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue; +} + +impl IntoDiagArg for DiagArgValue { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + self + } +} + +impl From for FluentValue<'static> { + fn from(val: DiagArgValue) -> Self { + match val { + DiagArgValue::Str(s) => From::from(s), + DiagArgValue::Number(n) => From::from(n), + DiagArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l), + } + } +} diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 3e8cf6207aed..7912b8e6098b 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -9,21 +9,17 @@ annotate-snippets = "0.11" derive_setters = "0.1.6" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_codes = { path = "../rustc_error_codes" } rustc_error_messages = { path = "../rustc_error_messages" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hashes = { path = "../rustc_hashes" } -rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } -rustc_target = { path = "../rustc_target" } -rustc_type_ir = { path = "../rustc_type_ir" } serde = { version = "1.0.125", features = ["derive"] } serde_json = "1.0.59" termcolor = "1.2.0" diff --git a/compiler/rustc_errors/src/codes.rs b/compiler/rustc_errors/src/codes.rs index 947cf27ca795..787a8af99b1f 100644 --- a/compiler/rustc_errors/src/codes.rs +++ b/compiler/rustc_errors/src/codes.rs @@ -20,6 +20,8 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } +rustc_error_messages::into_diag_arg_using_display!(ErrCode); + macro_rules! define_error_code_constants_and_diagnostics_table { ($($name:ident: $num:literal,)*) => ( $( diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs new file mode 100644 index 000000000000..5aef26ccf973 --- /dev/null +++ b/compiler/rustc_errors/src/decorate_diag.rs @@ -0,0 +1,85 @@ +/// This module provides types and traits for buffering lints until later in compilation. +use rustc_ast::node_id::NodeId; +use rustc_data_structures::fx::FxIndexMap; +use rustc_error_messages::MultiSpan; +use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId}; + +use crate::{DynSend, LintDiagnostic, LintDiagnosticBox}; + +/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its +/// variants requires types we don't have yet. So, handle that case separately. +pub enum DecorateDiagCompat { + Dynamic(Box LintDiagnosticBox<'a, ()> + DynSend + 'static>), + Builtin(BuiltinLintDiag), +} + +impl std::fmt::Debug for DecorateDiagCompat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DecorateDiagCompat").finish() + } +} + +impl !LintDiagnostic<'_, ()> for BuiltinLintDiag {} + +impl LintDiagnostic<'a, ()> + DynSend + 'static> From for DecorateDiagCompat { + #[inline] + fn from(d: D) -> Self { + Self::Dynamic(Box::new(d)) + } +} + +impl From for DecorateDiagCompat { + #[inline] + fn from(b: BuiltinLintDiag) -> Self { + Self::Builtin(b) + } +} + +/// Lints that are buffered up early on in the `Session` before the +/// `LintLevels` is calculated. +#[derive(Debug)] +pub struct BufferedEarlyLint { + /// The span of code that we are linting on. + pub span: Option, + + /// The `NodeId` of the AST node that generated the lint. + pub node_id: NodeId, + + /// A lint Id that can be passed to + /// `rustc_lint::early::EarlyContextAndPass::check_id`. + pub lint_id: LintId, + + /// Customization of the `Diag<'_>` for the lint. + pub diagnostic: DecorateDiagCompat, +} + +#[derive(Default, Debug)] +pub struct LintBuffer { + pub map: FxIndexMap>, +} + +impl LintBuffer { + pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { + self.map.entry(early_lint.node_id).or_default().push(early_lint); + } + + pub fn take(&mut self, id: NodeId) -> Vec { + // FIXME(#120456) - is `swap_remove` correct? + self.map.swap_remove(&id).unwrap_or_default() + } + + pub fn buffer_lint( + &mut self, + lint: &'static Lint, + node_id: NodeId, + span: impl Into, + decorate: impl Into, + ) { + self.add_early_lint(BufferedEarlyLint { + lint_id: LintId::of(lint), + node_id, + span: Some(span.into()), + diagnostic: decorate.into(), + }); + } +} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index e579370ce4ef..96a4ed3218fb 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -8,7 +8,7 @@ use std::thread::panicking; use rustc_data_structures::fx::FxIndexMap; -use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and}; +use rustc_error_messages::{DiagArgName, DiagArgValue, IntoDiagArg}; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; use rustc_span::source_map::Spanned; @@ -22,26 +22,6 @@ Suggestions, }; -/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of -/// `DiagArg` are converted to `FluentArgs` (consuming the collection) at the start of diagnostic -/// emission. -pub type DiagArg<'iter> = (&'iter DiagArgName, &'iter DiagArgValue); - -/// Name of a diagnostic argument. -pub type DiagArgName = Cow<'static, str>; - -/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted -/// to a `FluentValue` by the emitter to be used in diagnostic translation. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] -pub enum DiagArgValue { - Str(Cow<'static, str>), - // This gets converted to a `FluentNumber`, which is an `f64`. An `i32` - // safely fits in an `f64`. Any integers bigger than that will be converted - // to strings in `into_diag_arg` and stored using the `Str` variant. - Number(i32), - StrListSepByAnd(Vec>), -} - pub type DiagArgMap = FxIndexMap; /// Trait for types that `Diag::emit` can return as a "guarantee" (or "proof") @@ -143,36 +123,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { } } -/// Converts a value of a type into a `DiagArg` (typically a field of an `Diag` struct). -/// Implemented as a custom trait rather than `From` so that it is implemented on the type being -/// converted rather than on `DiagArgValue`, which enables types from other `rustc_*` crates to -/// implement this. -pub trait IntoDiagArg { - /// Convert `Self` into a `DiagArgValue` suitable for rendering in a diagnostic. - /// - /// It takes a `path` where "long values" could be written to, if the `DiagArgValue` is too big - /// for displaying on the terminal. This path comes from the `Diag` itself. When rendering - /// values that come from `TyCtxt`, like `Ty<'_>`, they can use `TyCtxt::short_string`. If a - /// value has no shortening logic that could be used, the argument can be safely ignored. - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue; -} - -impl IntoDiagArg for DiagArgValue { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - self - } -} - -impl From for FluentValue<'static> { - fn from(val: DiagArgValue) -> Self { - match val { - DiagArgValue::Str(s) => From::from(s), - DiagArgValue::Number(n) => From::from(n), - DiagArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l), - } - } -} - /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. #[rustc_diagnostic_item = "Subdiagnostic"] @@ -188,10 +138,20 @@ pub trait Subdiagnostic /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. #[rustc_diagnostic_item = "LintDiagnostic"] pub trait LintDiagnostic<'a, G: EmissionGuarantee> { - /// Decorate and emit a lint. + /// Decorate a lint with the information from this type. fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>); } +pub trait LintDiagnosticBox<'a, G: EmissionGuarantee> { + fn decorate_lint_box<'b>(self: Box, diag: &'b mut Diag<'a, G>); +} + +impl<'a, G: EmissionGuarantee, D: LintDiagnostic<'a, G>> LintDiagnosticBox<'a, G> for D { + fn decorate_lint_box<'b>(self: Box, diag: &'b mut Diag<'a, G>) { + self.decorate_lint(diag); + } +} + #[derive(Clone, Debug, Encodable, Decodable)] pub(crate) struct DiagLocation { file: Cow<'static, str>, @@ -617,6 +577,29 @@ pub fn downgrade_to_delayed_bug(&mut self) { self.level = Level::DelayedBug; } + /// Make emitting this diagnostic fatal + /// + /// Changes the level of this diagnostic to Fatal, and importantly also changes the emission guarantee. + /// This is sound for errors that would otherwise be printed, but now simply exit the process instead. + /// This function still gives an emission guarantee, the guarantee is now just that it exits fatally. + /// For delayed bugs this is different, since those are buffered. If we upgrade one to fatal, another + /// might now be ignored. + #[rustc_lint_diagnostics] + #[track_caller] + pub fn upgrade_to_fatal(mut self) -> Diag<'a, FatalAbort> { + assert!( + matches!(self.level, Level::Error), + "upgrade_to_fatal: cannot upgrade {:?} to Fatal: not an error", + self.level + ); + self.level = Level::Fatal; + + // Take is okay since we immediately rewrap it in another diagnostic. + // i.e. we do emit it despite defusing the original diagnostic's drop bomb. + let diag = self.diag.take(); + Diag { dcx: self.dcx, diag, _marker: PhantomData } + } + with_fn! { with_span_label, /// Appends a labeled span to the diagnostic. /// diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index eca5806fac52..435d16a83806 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,317 +1,22 @@ -use std::backtrace::Backtrace; use std::borrow::Cow; -use std::fmt; -use std::num::ParseIntError; -use std::path::{Path, PathBuf}; -use std::process::ExitStatus; use rustc_abi::TargetDataLayoutErrors; -use rustc_ast::util::parser::ExprPrecedence; -use rustc_ast_pretty::pprust; -use rustc_hir::RustcVersion; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::Subdiagnostic; -use rustc_span::edition::Edition; -use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; -use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTuple}; -use rustc_type_ir::{ClosureKind, FloatTy}; -use {rustc_ast as ast, rustc_hir as hir}; +use rustc_span::{Span, Symbol}; use crate::diagnostic::DiagLocation; use crate::{ - Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level, - Subdiagnostic, fluent_generated as fluent, + Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic, + fluent_generated as fluent, }; -pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display); - -impl IntoDiagArg for DiagArgFromDisplay<'_> { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.0.to_string().into_diag_arg(path) - } -} - -impl<'a> From<&'a dyn fmt::Display> for DiagArgFromDisplay<'a> { - fn from(t: &'a dyn fmt::Display) -> Self { - DiagArgFromDisplay(t) - } -} - -impl<'a, T: fmt::Display> From<&'a T> for DiagArgFromDisplay<'a> { - fn from(t: &'a T) -> Self { - DiagArgFromDisplay(t) - } -} - -impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.clone().into_diag_arg(path) - } -} - -#[macro_export] -macro_rules! into_diag_arg_using_display { - ($( $ty:ty ),+ $(,)?) => { - $( - impl IntoDiagArg for $ty { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_string().into_diag_arg(path) - } - } - )+ - } -} - -macro_rules! into_diag_arg_for_number { - ($( $ty:ty ),+ $(,)?) => { - $( - impl IntoDiagArg for $ty { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - // Convert to a string if it won't fit into `Number`. - #[allow(irrefutable_let_patterns)] - if let Ok(n) = TryInto::::try_into(self) { - DiagArgValue::Number(n) - } else { - self.to_string().into_diag_arg(path) - } - } - } - )+ - } -} - -into_diag_arg_using_display!( - ast::ParamKindOrd, - std::io::Error, - Box, - std::num::NonZero, - hir::Target, - Edition, - Ident, - MacroRulesNormalizedIdent, - ParseIntError, - StackProtector, - &TargetTuple, - SplitDebuginfo, - ExitStatus, - ErrCode, - rustc_abi::ExternAbi, -); - -impl IntoDiagArg for RustcVersion { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagArg for rustc_type_ir::TraitRef { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_string().into_diag_arg(path) - } -} - -impl IntoDiagArg for rustc_type_ir::ExistentialTraitRef { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_string().into_diag_arg(path) - } -} - -impl IntoDiagArg for rustc_type_ir::UnevaluatedConst { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - format!("{self:?}").into_diag_arg(path) - } -} - -impl IntoDiagArg for rustc_type_ir::FnSig { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - format!("{self:?}").into_diag_arg(path) - } -} - -impl IntoDiagArg for rustc_type_ir::Binder -where - T: IntoDiagArg, -{ - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.skip_binder().into_diag_arg(path) - } -} - -into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); - -impl IntoDiagArg for bool { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - if self { - DiagArgValue::Str(Cow::Borrowed("true")) - } else { - DiagArgValue::Str(Cow::Borrowed("false")) - } - } -} - -impl IntoDiagArg for char { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(format!("{self:?}"))) - } -} - -impl IntoDiagArg for Vec { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::StrListSepByAnd( - self.into_iter().map(|c| Cow::Owned(format!("{c:?}"))).collect(), - ) - } -} - -impl IntoDiagArg for Symbol { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_ident_string().into_diag_arg(path) - } -} - -impl<'a> IntoDiagArg for &'a str { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_string().into_diag_arg(path) - } -} - -impl IntoDiagArg for String { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self)) - } -} - -impl<'a> IntoDiagArg for Cow<'a, str> { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.into_owned())) - } -} - -impl<'a> IntoDiagArg for &'a Path { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.display().to_string())) - } -} - -impl IntoDiagArg for PathBuf { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.display().to_string())) - } -} - -impl IntoDiagArg for PanicStrategy { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.desc().to_string())) - } -} - -impl IntoDiagArg for hir::ConstContext { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(match self { - hir::ConstContext::ConstFn => "const_fn", - hir::ConstContext::Static(_) => "static", - hir::ConstContext::Const { .. } => "const", - })) - } -} - -impl IntoDiagArg for ast::Expr { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(pprust::expr_to_string(&self))) - } -} - -impl IntoDiagArg for ast::Path { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) - } -} - -impl IntoDiagArg for ast::token::Token { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(pprust::token_to_string(&self)) - } -} - -impl IntoDiagArg for ast::token::TokenKind { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(pprust::token_kind_to_string(&self)) - } -} - -impl IntoDiagArg for FloatTy { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.name_str())) - } -} - -impl IntoDiagArg for std::ffi::CString { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) - } -} - -impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) - } -} - -impl IntoDiagArg for ast::Visibility { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - let s = pprust::vis_to_string(&self); - let s = s.trim_end().to_string(); - DiagArgValue::Str(Cow::Owned(s)) - } -} - -impl IntoDiagArg for rustc_lint_defs::Level { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.to_cmd_flag())) - } -} - -impl IntoDiagArg for hir::def::Res { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.descr())) - } -} - impl IntoDiagArg for DiagLocation { fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { DiagArgValue::Str(Cow::from(self.to_string())) } } -impl IntoDiagArg for Backtrace { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::from(self.to_string())) - } -} - -impl IntoDiagArg for Level { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::from(self.to_string())) - } -} - -impl IntoDiagArg for ClosureKind { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(self.as_str().into()) - } -} - -impl IntoDiagArg for hir::def::Namespace { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.descr())) - } -} - -impl IntoDiagArg for ExprPrecedence { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Number(self as i32) - } -} - #[derive(Clone)] pub struct DiagSymbolList(Vec); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 97c47fa9b9a5..749bba5de127 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use derive_setters::Setters; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{DynSend, IntoDynSyncSend}; use rustc_error_messages::{FluentArgs, SpanLabel}; use rustc_lexer; @@ -1853,7 +1853,7 @@ fn emit_messages_default_inner( && line_idx + 1 == annotated_file.lines.len(), ); - let mut to_add = FxHashMap::default(); + let mut to_add = FxIndexMap::default(); for (depth, style) in depths { // FIXME(#120456) - is `swap_remove` correct? diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 2534cddf1057..71fc54f0d33f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -40,13 +40,13 @@ use Level::*; pub use codes::*; +pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ - BugAbort, Diag, DiagArg, DiagArgMap, DiagArgName, DiagArgValue, DiagInner, DiagStyledString, - Diagnostic, EmissionGuarantee, FatalAbort, IntoDiagArg, LintDiagnostic, StringPart, Subdiag, - Subdiagnostic, + BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, + FatalAbort, LintDiagnostic, LintDiagnosticBox, StringPart, Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ - DiagArgFromDisplay, DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, + DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, IndicateAnonymousLifetime, SingleLabelManySpans, }; pub use emitter::ColorConfig; @@ -56,11 +56,11 @@ use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{DynSend, Lock}; pub use rustc_error_messages::{ - DiagMessage, FluentBundle, LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, - SubdiagMessage, fallback_fluent_bundle, fluent_bundle, + DiagArg, DiagArgFromDisplay, DiagArgName, DiagArgValue, DiagMessage, FluentBundle, IntoDiagArg, + LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagMessage, + fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display, }; use rustc_hashes::Hash128; -use rustc_hir::HirId; pub use rustc_lint_defs::{Applicability, listify, pluralize}; use rustc_lint_defs::{Lint, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; @@ -80,6 +80,7 @@ pub mod annotate_snippet_emitter_writer; pub mod codes; +mod decorate_diag; mod diagnostic; mod diagnostic_impls; pub mod emitter; @@ -108,13 +109,14 @@ /// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`. /// Always the `TyCtxt`. pub trait LintEmitter: Copy { + type Id: Copy; #[track_caller] fn emit_node_span_lint( self, lint: &'static Lint, - hir_id: HirId, + hir_id: Self::Id, span: impl Into, - decorator: impl for<'a> LintDiagnostic<'a, ()>, + decorator: impl for<'a> LintDiagnostic<'a, ()> + DynSend + 'static, ); } @@ -1999,6 +2001,12 @@ fn can_be_subdiag(&self) -> bool { } } +impl IntoDiagArg for Level { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::from(self.to_string())) + } +} + // FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite. pub fn elided_lifetime_in_path_suggestion( source_map: &SourceMap, diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 1f8f3be68092..61ba716d082b 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -70,7 +70,7 @@ expand_invalid_fragment_specifier = invalid fragment specifier `{$fragment}` .help = {$help} -expand_macro_args_bad_delim = macro attribute argument matchers require parentheses +expand_macro_args_bad_delim = `{$rule_kw}` rule argument matchers require parentheses expand_macro_args_bad_delim_sugg = the delimiters should be `(` and `)` expand_macro_body_stability = diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index f2c150715329..8ff21509f4a9 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -13,13 +13,13 @@ use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; +use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation}; use rustc_hir::def::MacroKinds; use rustc_hir::{Stability, find_attr}; -use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; +use rustc_lint_defs::RegisteredTools; use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::config::CollapseMacroDebuginfo; diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 83a8d601afea..15419ab7423f 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -11,8 +11,10 @@ NodeId, NormalAttr, }; use rustc_attr_parsing as attr; +use rustc_attr_parsing::validate_attr::deny_builtin_meta_unsafety; use rustc_attr_parsing::{ AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg_attr, + validate_attr, }; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ @@ -20,8 +22,6 @@ REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES, }; use rustc_lint_defs::BuiltinLintDiag; -use rustc_parse::validate_attr; -use rustc_parse::validate_attr::deny_builtin_meta_unsafety; use rustc_session::Session; use rustc_session::parse::feature_err; use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; @@ -415,16 +415,6 @@ pub(crate) fn cfg_true( node: NodeId, emit_errors: ShouldEmit, ) -> EvalConfigResult { - // We need to run this to do basic validation of the attribute, such as that lits are valid, etc - // FIXME(jdonszelmann) this should not be necessary in the future - match validate_attr::parse_meta(&self.sess.psess, attr) { - Ok(_) => {} - Err(err) => { - err.emit(); - return EvalConfigResult::True; - } - } - // Unsafety check needs to be done explicitly here because this attribute will be removed before the normal check deny_builtin_meta_unsafety( self.sess.dcx(), diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index e58269991fcb..ba9d76970f0c 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -490,6 +490,7 @@ pub(crate) struct MacroArgsBadDelim { pub span: Span, #[subdiagnostic] pub sugg: MacroArgsBadDelimSugg, + pub rule_kw: Symbol, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 670f5c91bb93..012bfe226f26 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,26 +1,28 @@ use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; -use std::{iter, mem}; +use std::{iter, mem, slice}; use rustc_ast::mut_visit::*; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ - self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, DUMMY_NODE_ID, - ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, - MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, + self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, CRATE_NODE_ID, + DUMMY_NODE_ID, ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, + MetaItemInner, MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; -use rustc_attr_parsing::{EvalConfigResult, ShouldEmit}; +use rustc_attr_parsing::{AttributeParser, EvalConfigResult, ShouldEmit, validate_attr}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::PResult; use rustc_feature::Features; +use rustc_hir::Target; +use rustc_hir::def::MacroKinds; use rustc_parse::parser::{ AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma, token_descr, }; -use rustc_parse::validate_attr; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; use rustc_session::parse::feature_err; @@ -565,6 +567,7 @@ pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragm .map(|DeriveResolution { path, item, exts: _, is_const }| { // FIXME: Consider using the derive resolutions (`_exts`) // instead of enqueuing the derives to be resolved again later. + // Note that this can result in duplicate diagnostics. let expn_id = LocalExpnId::fresh_empty(); derive_invocations.push(( Invocation { @@ -799,7 +802,8 @@ fn expand_invoc( ItemKind::Mod( _, _, - ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _, _), + ModKind::Unloaded + | ModKind::Loaded(_, Inline::No { .. }, _), ) ) => { @@ -922,6 +926,35 @@ fn expand_invoc( } fragment } + SyntaxExtensionKind::MacroRules(expander) + if expander.kinds().contains(MacroKinds::DERIVE) => + { + if is_const { + let guar = self + .cx + .dcx() + .span_err(span, "macro `derive` does not support const derives"); + return ExpandResult::Ready(fragment_kind.dummy(span, guar)); + } + let body = item.to_tokens(); + match expander.expand_derive(self.cx, span, &body) { + Ok(tok_result) => { + let fragment = + self.parse_ast_fragment(tok_result, fragment_kind, &path, span); + if macro_stats { + update_derive_macro_stats( + self.cx, + fragment_kind, + span, + &path, + &fragment, + ); + } + fragment + } + Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), + } + } _ => unreachable!(), }, InvocationKind::GlobDelegation { item, of_trait } => { @@ -1004,7 +1037,7 @@ impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> { fn visit_item(&mut self, item: &'ast ast::Item) { match &item.kind { ItemKind::Mod(_, _, mod_kind) - if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) => + if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) => { feature_err( self.sess, @@ -1315,7 +1348,7 @@ fn wrap_flat_map_node_walk_flat_map( let ItemKind::Mod(_, ident, ref mut mod_kind) = node.kind else { unreachable!() }; let ecx = &mut collector.cx; let (file_path, dir_path, dir_ownership) = match mod_kind { - ModKind::Loaded(_, inline, _, _) => { + ModKind::Loaded(_, inline, _) => { // Inline `mod foo { ... }`, but we still need to push directories. let (dir_path, dir_ownership) = mod_dir_path( ecx.sess, @@ -1329,7 +1362,7 @@ fn wrap_flat_map_node_walk_flat_map( // This lets `parse_external_mod` catch cycles if it's self-referential. let file_path = match inline { Inline::Yes => None, - Inline::No => mod_file_path_from_attr(ecx.sess, &attrs, &dir_path), + Inline::No { .. } => mod_file_path_from_attr(ecx.sess, &attrs, &dir_path), }; node.attrs = attrs; (file_path, dir_path, dir_ownership) @@ -1365,7 +1398,7 @@ fn wrap_flat_map_node_walk_flat_map( ); } - *mod_kind = ModKind::Loaded(items, Inline::No, spans, had_parse_error); + *mod_kind = ModKind::Loaded(items, Inline::No { had_parse_error }, spans); node.attrs = attrs; if node.attrs.len() > old_attrs_len { // If we loaded an out-of-line module and added some inner attributes, @@ -2126,6 +2159,16 @@ fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { attr, self.cx.current_expansion.lint_node_id, ); + AttributeParser::parse_limited_all( + self.cx.sess, + slice::from_ref(attr), + None, + Target::MacroCall, + call.span(), + CRATE_NODE_ID, + Some(self.cx.ecfg.features), + ShouldEmit::ErrorsAndLints, + ); let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; span = Some(current_span); @@ -2437,7 +2480,7 @@ fn visit_expr(&mut self, node: &mut ast::Expr) { if let Some(attr) = node.attrs.first() { self.cfg().maybe_emit_expr_attr_err(attr); } - self.visit_node(node) + ensure_sufficient_stack(|| self.visit_node(node)) } fn visit_method_receiver_expr(&mut self, node: &mut ast::Expr) { diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 80433b7be910..f5edaf50edd5 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -14,14 +14,22 @@ use crate::expand::{AstFragmentKind, parse_ast_fragment}; use crate::mbe::macro_parser::ParseResult::*; use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser}; -use crate::mbe::macro_rules::{Tracker, try_match_macro, try_match_macro_attr}; +use crate::mbe::macro_rules::{ + Tracker, try_match_macro, try_match_macro_attr, try_match_macro_derive, +}; + +pub(super) enum FailedMacro<'a> { + Func, + Attr(&'a TokenStream), + Derive, +} pub(super) fn failed_to_match_macro( psess: &ParseSess, sp: Span, def_span: Span, name: Ident, - attr_args: Option<&TokenStream>, + args: FailedMacro<'_>, body: &TokenStream, rules: &[MacroRule], ) -> (Span, ErrorGuaranteed) { @@ -36,10 +44,12 @@ pub(super) fn failed_to_match_macro( // diagnostics. let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp); - let try_success_result = if let Some(attr_args) = attr_args { - try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker) - } else { - try_match_macro(psess, name, body, rules, &mut tracker) + let try_success_result = match args { + FailedMacro::Func => try_match_macro(psess, name, body, rules, &mut tracker), + FailedMacro::Attr(attr_args) => { + try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker) + } + FailedMacro::Derive => try_match_macro_derive(psess, name, body, rules, &mut tracker), }; if try_success_result.is_ok() { @@ -90,7 +100,7 @@ pub(super) fn failed_to_match_macro( } // Check whether there's a missing comma in this macro call, like `println!("{}" a);` - if attr_args.is_none() + if let FailedMacro::Func = args && let Some((body, comma_span)) = body.add_comma() { for rule in rules { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 334f57f9d625..8b43f852b263 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -27,10 +27,10 @@ use rustc_session::parse::{ParseSess, feature_err}; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::{debug, instrument, trace, trace_span}; -use super::diagnostics::failed_to_match_macro; +use super::diagnostics::{FailedMacro, failed_to_match_macro}; use super::macro_parser::{NamedMatches, NamedParseResult}; use super::{SequenceRepetition, diagnostics}; use crate::base::{ @@ -138,6 +138,8 @@ pub(super) enum MacroRule { body_span: Span, rhs: mbe::TokenTree, }, + /// A derive rule, for use with `#[m]` + Derive { body: Vec, body_span: Span, rhs: mbe::TokenTree }, } pub struct MacroRulesMacroExpander { @@ -157,6 +159,7 @@ pub fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, MultiSpan)> { MacroRule::Attr { args_span, body_span, ref rhs, .. } => { (MultiSpan::from_spans(vec![args_span, body_span]), rhs) } + MacroRule::Derive { body_span, ref rhs, .. } => (MultiSpan::from_span(body_span), rhs), }; if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) } } @@ -164,6 +167,63 @@ pub fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, MultiSpan)> { pub fn kinds(&self) -> MacroKinds { self.kinds } + + pub fn expand_derive( + &self, + cx: &mut ExtCtxt<'_>, + sp: Span, + body: &TokenStream, + ) -> Result { + // This is similar to `expand_macro`, but they have very different signatures, and will + // diverge further once derives support arguments. + let Self { name, ref rules, node_id, .. } = *self; + let psess = &cx.sess.psess; + + if cx.trace_macros() { + let msg = format!("expanding `#[derive({name})] {}`", pprust::tts_to_string(body)); + trace_macros_note(&mut cx.expansions, sp, msg); + } + + match try_match_macro_derive(psess, name, body, rules, &mut NoopTracker) { + Ok((rule_index, rule, named_matches)) => { + let MacroRule::Derive { rhs, .. } = rule else { + panic!("try_match_macro_derive returned non-derive rule"); + }; + let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else { + cx.dcx().span_bug(sp, "malformed macro derive rhs"); + }; + + let id = cx.current_expansion.id; + let tts = transcribe(psess, &named_matches, rhs, *rhs_span, self.transparency, id) + .map_err(|e| e.emit())?; + + if cx.trace_macros() { + let msg = format!("to `{}`", pprust::tts_to_string(&tts)); + trace_macros_note(&mut cx.expansions, sp, msg); + } + + if is_defined_in_current_crate(node_id) { + cx.resolver.record_macro_rule_usage(node_id, rule_index); + } + + Ok(tts) + } + Err(CanRetry::No(guar)) => Err(guar), + Err(CanRetry::Yes) => { + let (_, guar) = failed_to_match_macro( + cx.psess(), + sp, + self.span, + name, + FailedMacro::Derive, + body, + rules, + ); + cx.macro_error_and_trace_macros_diag(); + Err(guar) + } + } + } } impl TTMacroExpander for MacroRulesMacroExpander { @@ -325,8 +385,15 @@ fn expand_macro<'cx>( } Err(CanRetry::Yes) => { // Retry and emit a better error. - let (span, guar) = - failed_to_match_macro(cx.psess(), sp, def_span, name, None, &arg, rules); + let (span, guar) = failed_to_match_macro( + cx.psess(), + sp, + def_span, + name, + FailedMacro::Func, + &arg, + rules, + ); cx.macro_error_and_trace_macros_diag(); DummyResult::any(span, guar) } @@ -388,8 +455,15 @@ fn expand_macro_attr( Err(CanRetry::No(guar)) => Err(guar), Err(CanRetry::Yes) => { // Retry and emit a better error. - let (_, guar) = - failed_to_match_macro(cx.psess(), sp, def_span, name, Some(&args), &body, rules); + let (_, guar) = failed_to_match_macro( + cx.psess(), + sp, + def_span, + name, + FailedMacro::Attr(&args), + &body, + rules, + ); cx.trace_macros_diag(); Err(guar) } @@ -522,6 +596,7 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>( match result { Success(body_named_matches) => { psess.gated_spans.merge(gated_spans_snapshot); + #[allow(rustc::potential_query_instability)] named_matches.extend(body_named_matches); return Ok((i, rule, named_matches)); } @@ -536,6 +611,44 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>( Err(CanRetry::Yes) } +/// Try expanding the macro derive. Returns the index of the successful arm and its +/// named_matches if it was successful, and nothing if it failed. On failure, it's the caller's job +/// to use `track` accordingly to record all errors correctly. +#[instrument(level = "debug", skip(psess, body, rules, track), fields(tracking = %T::description()))] +pub(super) fn try_match_macro_derive<'matcher, T: Tracker<'matcher>>( + psess: &ParseSess, + name: Ident, + body: &TokenStream, + rules: &'matcher [MacroRule], + track: &mut T, +) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> { + // This uses the same strategy as `try_match_macro` + let body_parser = parser_from_cx(psess, body.clone(), T::recovery()); + let mut tt_parser = TtParser::new(name); + for (i, rule) in rules.iter().enumerate() { + let MacroRule::Derive { body, .. } = rule else { continue }; + + let mut gated_spans_snapshot = mem::take(&mut *psess.gated_spans.spans.borrow_mut()); + + let result = tt_parser.parse_tt(&mut Cow::Borrowed(&body_parser), body, track); + track.after_arm(true, &result); + + match result { + Success(named_matches) => { + psess.gated_spans.merge(gated_spans_snapshot); + return Ok((i, rule, named_matches)); + } + Failure(_) => { + mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut()) + } + Error(_, _) => return Err(CanRetry::Yes), + ErrorReported(guar) => return Err(CanRetry::No(guar)), + } + } + + Err(CanRetry::Yes) +} + /// Converts a macro item into a syntax extension. pub fn compile_declarative_macro( sess: &Session, @@ -569,7 +682,7 @@ pub fn compile_declarative_macro( let mut rules = Vec::new(); while p.token != token::Eof { - let args = if p.eat_keyword_noexpect(sym::attr) { + let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) { kinds |= MacroKinds::ATTR; if !features.macro_attr() { feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable") @@ -579,16 +692,46 @@ pub fn compile_declarative_macro( return dummy_syn_ext(guar); } let args = p.parse_token_tree(); - check_args_parens(sess, &args); + check_args_parens(sess, sym::attr, &args); let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition); check_emission(check_lhs(sess, node_id, &args)); if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") { return dummy_syn_ext(guar); } - Some(args) + (Some(args), false) + } else if p.eat_keyword_noexpect(sym::derive) { + kinds |= MacroKinds::DERIVE; + let derive_keyword_span = p.prev_token.span; + if !features.macro_derive() { + feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable") + .emit(); + } + if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") { + return dummy_syn_ext(guar); + } + let args = p.parse_token_tree(); + check_args_parens(sess, sym::derive, &args); + let args_empty_result = check_args_empty(sess, &args); + let args_not_empty = args_empty_result.is_err(); + check_emission(args_empty_result); + if let Some(guar) = check_no_eof(sess, &p, "expected macro derive body") { + return dummy_syn_ext(guar); + } + // If the user has `=>` right after the `()`, they might have forgotten the empty + // parentheses. + if p.token == token::FatArrow { + let mut err = sess + .dcx() + .struct_span_err(p.token.span, "expected macro derive body, got `=>`"); + if args_not_empty { + err.span_label(derive_keyword_span, "need `()` after this `derive`"); + } + return dummy_syn_ext(err.emit()); + } + (None, true) } else { kinds |= MacroKinds::BANG; - None + (None, false) }; let lhs_tt = p.parse_token_tree(); let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition); @@ -619,6 +762,8 @@ pub fn compile_declarative_macro( let args = mbe::macro_parser::compute_locs(&delimited.tts); let body_span = lhs_span; rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt }); + } else if is_derive { + rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt }); } else { rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt }); } @@ -665,7 +810,7 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option Result<(), ErrorGuaranteed> { + match args { + tokenstream::TokenTree::Delimited(.., delimited) if delimited.is_empty() => Ok(()), + _ => { + let msg = "`derive` rules do not accept arguments; `derive` must be followed by `()`"; + Err(sess.dcx().span_err(args.span(), msg)) + } + } +} + fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> { let e1 = check_lhs_nt_follows(sess, node_id, lhs); let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs)); diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 662c67f2d3f2..19f3cdbc5491 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -2,8 +2,9 @@ use std::path::{self, Path, PathBuf}; use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans}; +use rustc_attr_parsing::validate_attr; use rustc_errors::{Diag, ErrorGuaranteed}; -use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal, validate_attr}; +use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::Session; use rustc_session::parse::ParseSess; use rustc_span::{Ident, Span, sym}; @@ -120,7 +121,7 @@ pub(crate) fn mod_dir_path( (dir_path, dir_ownership) } - Inline::No => { + Inline::No { .. } => { // FIXME: This is a subset of `parse_external_mod` without actual parsing, // check whether the logic for unloaded, loaded and inline modules can be unified. let file_path = mod_file_path(sess, ident, attrs, &module.dir_path, dir_ownership) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index fd71f2ce948c..5b1d3d6d35b6 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -250,12 +250,14 @@ fn from_internal((stream, rustc): (TokenStream, &mut Rustc<'_, '_>)) -> Self { Question => op("?"), SingleQuote => op("'"), - Ident(sym, is_raw) => { - trees.push(TokenTree::Ident(Ident { sym, is_raw: is_raw.into(), span })) - } + Ident(sym, is_raw) => trees.push(TokenTree::Ident(Ident { + sym, + is_raw: matches!(is_raw, IdentIsRaw::Yes), + span, + })), NtIdent(ident, is_raw) => trees.push(TokenTree::Ident(Ident { sym: ident.name, - is_raw: is_raw.into(), + is_raw: matches!(is_raw, IdentIsRaw::Yes), span: ident.span, })), @@ -263,7 +265,11 @@ fn from_internal((stream, rustc): (TokenStream, &mut Rustc<'_, '_>)) -> Self { let ident = rustc_span::Ident::new(name, span).without_first_quote(); trees.extend([ TokenTree::Punct(Punct { ch: b'\'', joint: true, span }), - TokenTree::Ident(Ident { sym: ident.name, is_raw: is_raw.into(), span }), + TokenTree::Ident(Ident { + sym: ident.name, + is_raw: matches!(is_raw, IdentIsRaw::Yes), + span, + }), ]); } NtLifetime(ident, is_raw) => { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 6fbedaf5b103..e81003b18972 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -6,6 +6,7 @@ use AttributeGate::*; use AttributeType::*; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::AttrStyle; use rustc_hir::attrs::EncodeCrossCrate; use rustc_span::edition::Edition; use rustc_span::{Symbol, sym}; @@ -132,9 +133,12 @@ pub struct AttributeTemplate { } impl AttributeTemplate { - pub fn suggestions(&self, inner: bool, name: impl std::fmt::Display) -> Vec { + pub fn suggestions(&self, style: AttrStyle, name: impl std::fmt::Display) -> Vec { let mut suggestions = vec![]; - let inner = if inner { "!" } else { "" }; + let inner = match style { + AttrStyle::Outer => "", + AttrStyle::Inner => "!", + }; if self.word { suggestions.push(format!("#{inner}[{name}]")); } @@ -741,9 +745,12 @@ pub struct BuiltinAttribute { ErrorPreceding, EncodeCrossCrate::No ), gated!( - no_sanitize, Normal, - template!(List: &["address, kcfi, memory, thread"]), DuplicatesOk, - EncodeCrossCrate::No, experimental!(no_sanitize) + unsafe force_target_feature, Normal, template!(List: &[r#"enable = "name""#]), + DuplicatesOk, EncodeCrossCrate::No, effective_target_features, experimental!(force_target_feature) + ), + gated!( + sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding, + EncodeCrossCrate::No, sanitize, experimental!(sanitize), ), gated!( coverage, Normal, template!(OneOf: &[sym::off, sym::on]), @@ -993,6 +1000,10 @@ pub struct BuiltinAttribute { rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, ), + rustc_attr!( + rustc_allocator_zeroed_variant, Normal, template!(NameValueStr: "function"), ErrorPreceding, + EncodeCrossCrate::Yes, + ), gated!( default_lib_allocator, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, allocator_internals, experimental!(default_lib_allocator), diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 04f261ada069..e37fc6b7bfcc 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -190,6 +190,9 @@ macro_rules! declare_features { (removed, no_coverage, "1.74.0", Some(84605), Some("renamed to `coverage_attribute`"), 114656), /// Allows `#[no_debug]`. (removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667), + // Allows the use of `no_sanitize` attribute. + /// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]` + (removed, no_sanitize, "CURRENT_RUSTC_VERSION", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681), /// Note: this feature was previously recorded in a separate /// `STABLE_REMOVED` list because it, uniquely, was once stable but was /// then removed. But there was no utility storing it separately, so now diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 07f928b8c882..92b435b4b017 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -480,6 +480,8 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, doc_cfg_hide, "1.57.0", Some(43781)), /// Allows `#[doc(masked)]`. (unstable, doc_masked, "1.21.0", Some(44027)), + /// Allows features to allow target_feature to better interact with traits. + (incomplete, effective_target_features, "CURRENT_RUSTC_VERSION", Some(143352)), /// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }` (incomplete, ergonomic_clones, "1.87.0", Some(132290)), /// Allows exhaustive pattern matching on types that contain uninhabited types. @@ -556,6 +558,8 @@ pub fn internal(&self, feature: Symbol) -> bool { (incomplete, loop_match, "1.90.0", Some(132306)), /// Allow `macro_rules!` attribute rules (unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)), + /// Allow `macro_rules!` derive rules + (unstable, macro_derive, "CURRENT_RUSTC_VERSION", Some(143549)), /// 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. @@ -592,8 +596,6 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, new_range, "1.86.0", Some(123741)), /// Allows `#![no_core]`. (unstable, no_core, "1.3.0", Some(29639)), - /// Allows the use of `no_sanitize` attribute. - (unstable, no_sanitize, "1.42.0", Some(39699)), /// Allows using the `non_exhaustive_omitted_patterns` lint. (unstable, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554)), /// Allows `for` binders in where-clauses @@ -614,6 +616,7 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows the use of raw-dylibs on ELF platforms (incomplete, raw_dylib_elf, "1.87.0", Some(135694)), + (unstable, reborrow, "CURRENT_RUSTC_VERSION", Some(145612)), /// 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)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant @@ -626,6 +629,8 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), + /// Allows the use of the `sanitize` attribute. + (unstable, sanitize, "CURRENT_RUSTC_VERSION", Some(39699)), /// 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). diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 71496b7ec324..1008a3e787d0 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -12,7 +12,9 @@ rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_error_messages = { path = "../rustc_error_messages" } rustc_hashes = { path = "../rustc_hashes" } +rustc_hir_id = { path = "../rustc_hir_id" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 510fc8329782..09da5772d231 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1,11 +1,16 @@ +use std::borrow::Cow; +use std::path::PathBuf; + pub use ReprAttr::*; use rustc_abi::Align; use rustc_ast::token::CommentKind; use rustc_ast::{AttrStyle, ast}; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; use rustc_span::{Ident, Span, Symbol}; +pub use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; use crate::attrs::pretty_printing::PrintAttribute; @@ -205,6 +210,44 @@ pub enum Linkage { WeakODR, } +#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum MirDialect { + Analysis, + Built, + Runtime, +} + +impl IntoDiagArg for MirDialect { + fn into_diag_arg(self, _path: &mut Option) -> DiagArgValue { + let arg = match self { + MirDialect::Analysis => "analysis", + MirDialect::Built => "built", + MirDialect::Runtime => "runtime", + }; + DiagArgValue::Str(Cow::Borrowed(arg)) + } +} + +#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum MirPhase { + Initial, + PostCleanup, + Optimized, +} + +impl IntoDiagArg for MirPhase { + fn into_diag_arg(self, _path: &mut Option) -> DiagArgValue { + let arg = match self { + MirPhase::Initial => "initial", + MirPhase::PostCleanup => "post-cleanup", + MirPhase::Optimized => "optimized", + }; + DiagArgValue::Str(Cow::Borrowed(arg)) + } +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -324,6 +367,12 @@ pub enum AttributeKind { /// Represents `#[coverage(..)]`. Coverage(Span, CoverageAttrKind), + /// Represents `#[crate_name = ...]` + CrateName { name: Symbol, name_span: Span, attr_span: Span, style: AttrStyle }, + + /// Represents `#[custom_mir]`. + CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span), + ///Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), @@ -460,6 +509,12 @@ pub enum AttributeKind { /// Represents `#[rustc_object_lifetime_default]`. RustcObjectLifetimeDefault, + /// Represents `#[sanitize]` + /// + /// the on set and off set are distjoint since there's a third option: unset. + /// a node may not set the sanitizer setting in which case it inherits from parents. + Sanitize { on_set: SanitizerSet, off_set: SanitizerSet, span: Span }, + /// Represents `#[should_panic]` ShouldPanic { reason: Option, span: Span }, @@ -479,8 +534,9 @@ pub enum AttributeKind { /// Represents `#[rustc_std_internal_symbol]`. StdInternalSymbol(Span), - /// Represents `#[target_feature(enable = "...")]` - TargetFeature(ThinVec<(Symbol, Span)>, Span), + /// Represents `#[target_feature(enable = "...")]` and + /// `#[unsafe(force_target_feature(enable = "...")]`. + TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool }, /// Represents `#[track_caller]` TrackCaller(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 84a975523f2c..e5329c104bb5 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -7,6 +7,11 @@ pub enum EncodeCrossCrate { } impl AttributeKind { + /// Whether this attribute should be encoded in metadata files. + /// + /// If this is "Yes", then another crate can do `tcx.get_all_attrs(did)` for a did in this crate, and get the attribute. + /// When this is No, the attribute is filtered out while encoding and other crate won't be able to observe it. + /// This can be unexpectedly good for performance, so unless necessary for cross-crate compilation, prefer No. pub fn encode_cross_crate(&self) -> EncodeCrossCrate { use AttributeKind::*; use EncodeCrossCrate::*; @@ -31,6 +36,8 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { ConstTrait(..) => No, Coroutine(..) => No, Coverage(..) => No, + CrateName { .. } => No, + CustomMir(_, _, _) => Yes, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotImplementViaObject(..) => No, @@ -72,12 +79,13 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, + Sanitize { .. } => No, ShouldPanic { .. } => No, SkipDuringMethodDispatch { .. } => No, SpecializationTrait(..) => No, Stability { .. } => Yes, StdInternalSymbol(..) => No, - TargetFeature(..) => No, + TargetFeature { .. } => No, TrackCaller(..) => Yes, TypeConst(..) => Yes, UnsafeSpecializationMarker(..) => No, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index e44b29141da9..e65de25b4511 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -6,6 +6,7 @@ use rustc_ast_pretty::pp::Printer; use rustc_span::hygiene::Transparency; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; +use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; /// This trait is used to print attributes in `rustc_hir_pretty`. @@ -146,4 +147,14 @@ fn print_attribute(&self, p: &mut Printer) { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); print_disp!(u16, bool, NonZero); -print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); +print_debug!( + Symbol, + Ident, + UintTy, + IntTy, + Align, + AttrStyle, + CommentKind, + Transparency, + SanitizerSet, +); diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 79319e242660..8af4740f376b 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -1,10 +1,12 @@ use std::array::IntoIter; +use std::borrow::Cow; use std::fmt::Debug; use rustc_ast as ast; use rustc_ast::NodeId; use rustc_data_structures::stable_hasher::ToStableHashKey; use rustc_data_structures::unord::UnordMap; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::Symbol; use rustc_span::def_id::{DefId, LocalDefId}; @@ -586,6 +588,12 @@ pub enum Res { Err, } +impl IntoDiagArg for Res { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.descr())) + } +} + /// The result of resolving a path before lowering to HIR, /// with "module" segments resolved and associated item /// segments deferred to type checking. @@ -673,6 +681,12 @@ pub fn descr(self) -> &'static str { } } +impl IntoDiagArg for Namespace { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.descr())) + } +} + impl ToStableHashKey for Namespace { type KeyType = Namespace; diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2c8986b7c7d0..e397c286de28 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,4 +1,5 @@ // ignore-tidy-filelength +use std::borrow::Cow; use std::fmt; use rustc_abi::ExternAbi; @@ -17,6 +18,7 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::tagged_ptr::TaggedRef; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::LocalDefId; @@ -1159,6 +1161,12 @@ pub struct AttrPath { pub span: Span, } +impl IntoDiagArg for AttrPath { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.to_string().into_diag_arg(path) + } +} + impl AttrPath { pub fn from_ast(path: &ast::Path) -> Self { AttrPath { @@ -2259,8 +2267,15 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } -// NOTE: `IntoDiagArg` impl for `ConstContext` lives in `rustc_errors` -// due to a cyclical dependency between hir and that crate. +impl IntoDiagArg for ConstContext { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(match self { + ConstContext::ConstFn => "const_fn", + ConstContext::Static(_) => "static", + ConstContext::Const { .. } => "const", + })) + } +} /// A literal. pub type Lit = Spanned; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 905b84a8cbeb..0464665b41fc 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -437,6 +437,9 @@ pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> { DefaultTrait1, sym::default_trait1, default_trait1_trait, Target::Trait, GenericRequirement::None; ContractCheckEnsures, sym::contract_check_ensures, contract_check_ensures_fn, Target::Fn, GenericRequirement::None; + + // Reborrowing related lang-items + Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index f1212d07ff60..78fc63753a2e 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -3,14 +3,11 @@ //! [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(debug_closure_helpers)] #![feature(exhaustive_patterns)] -#![feature(negative_impls)] #![feature(never_type)] -#![feature(rustc_attrs)] #![feature(variant_count)] #![recursion_limit = "256"] // tidy-alphabetical-end @@ -25,7 +22,7 @@ pub mod diagnostic_items; pub use rustc_span::def_id; mod hir; -pub mod hir_id; +pub use rustc_hir_id::{self as hir_id, *}; pub mod intravisit; pub mod lang_items; pub mod lints; @@ -41,7 +38,6 @@ #[doc(no_inline)] pub use hir::*; -pub use hir_id::*; pub use lang_items::{LangItem, LanguageItems}; pub use stability::*; pub use stable_hash_impls::HashStableContext; diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index e3cde2d3bb67..061ec786dc80 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_macros::HashStable_Generic; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; -use crate::{HirId, Target}; +use crate::{AttrPath, HirId, Target}; #[derive(Debug)] pub struct DelayedLints { @@ -34,5 +34,5 @@ pub enum AttributeLintKind { UnusedDuplicate { this: Span, other: Span, warning: bool }, IllFormedAttributeInput { suggestions: Vec }, EmptyAttribute { first_span: Span }, - InvalidTarget { name: Symbol, target: Target, applied: String, only: &'static str }, + InvalidTarget { name: AttrPath, target: Target, applied: Vec, only: &'static str }, } diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index ecc608d437b8..16e8bac3d8a4 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -5,7 +5,7 @@ use crate::hir::{ AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, }; -use crate::hir_id::{HirId, ItemLocalId}; +use crate::hir_id::ItemLocalId; use crate::lints::DelayedLints; /// Requirements for a `StableHashingContext` to be used in this crate. @@ -15,25 +15,6 @@ pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStabl fn hash_attr_id(&mut self, id: &HashIgnoredAttrId, hasher: &mut StableHasher); } -impl ToStableHashKey for HirId { - type KeyType = (DefPathHash, ItemLocalId); - - #[inline] - fn to_stable_hash_key(&self, hcx: &HirCtx) -> (DefPathHash, ItemLocalId) { - let def_path_hash = self.owner.def_id.to_stable_hash_key(hcx); - (def_path_hash, self.local_id) - } -} - -impl ToStableHashKey for ItemLocalId { - type KeyType = ItemLocalId; - - #[inline] - fn to_stable_hash_key(&self, _: &HirCtx) -> ItemLocalId { - *self - } -} - impl ToStableHashKey for BodyId { type KeyType = (DefPathHash, ItemLocalId); diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index f68dad3a5e86..dcac51b10b49 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -79,6 +79,8 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } +rustc_error_messages::into_diag_arg_using_display!(Target); + impl Target { pub fn is_associated_item(self) -> bool { match self { diff --git a/compiler/rustc_hir/src/version.rs b/compiler/rustc_hir/src/version.rs index ab5ab026b4ca..bc2c38a49350 100644 --- a/compiler/rustc_hir/src/version.rs +++ b/compiler/rustc_hir/src/version.rs @@ -1,6 +1,8 @@ +use std::borrow::Cow; use std::fmt::{self, Display}; use std::sync::OnceLock; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{ Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version, }; @@ -45,3 +47,9 @@ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch) } } + +impl IntoDiagArg for RustcVersion { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.to_string())) + } +} diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 161a8566b04f..eccb88a938fb 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -2053,3 +2053,29 @@ pub(super) fn check_coroutine_obligations( Ok(()) } + +pub(super) fn check_potentially_region_dependent_goals<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + if !tcx.next_trait_solver_globally() { + return Ok(()); + } + let typeck_results = tcx.typeck(def_id); + let param_env = tcx.param_env(def_id); + + // We use `TypingMode::Borrowck` as we want to use the opaque types computed by HIR typeck. + let typing_mode = TypingMode::borrowck(tcx, def_id); + let infcx = tcx.infer_ctxt().ignoring_regions().build(typing_mode); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + for (predicate, cause) in &typeck_results.potentially_region_dependent_goals { + let predicate = fold_regions(tcx, *predicate, |_, _| { + infcx.next_region_var(RegionVariableOrigin::Misc(cause.span)) + }); + ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate)); + } + + let errors = ocx.select_all_or_error(); + debug!(?errors); + if errors.is_empty() { Ok(()) } else { Err(infcx.err_ctxt().report_fulfillment_errors(errors)) } +} diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index f50aed0b3c23..cfc6bc2f3a0a 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -136,6 +136,10 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::round_ties_even_f64 | sym::round_ties_even_f128 | sym::autodiff + | sym::prefetch_read_data + | sym::prefetch_write_data + | sym::prefetch_read_instruction + | sym::prefetch_write_instruction | sym::const_eval_select => hir::Safety::Safe, _ => hir::Safety::Unsafe, }; @@ -218,7 +222,7 @@ pub(crate) fn check_intrinsic_type( | sym::prefetch_write_data | sym::prefetch_read_instruction | sym::prefetch_write_instruction => { - (1, 0, vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.i32], tcx.types.unit) + (1, 1, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.unit) } sym::needs_drop => (1, 0, vec![], tcx.types.bool), diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 85445cb3c004..2e4b151d4dc8 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -109,6 +109,7 @@ pub(super) fn provide(providers: &mut Providers) { collect_return_position_impl_trait_in_trait_tys, compare_impl_item: compare_impl_item::compare_impl_item, check_coroutine_obligations: check::check_coroutine_obligations, + check_potentially_region_dependent_goals: check::check_potentially_region_dependent_goals, check_type_wf: wfcheck::check_type_wf, check_well_formed: wfcheck::check_well_formed, ..*providers diff --git a/compiler/rustc_hir_id/Cargo.toml b/compiler/rustc_hir_id/Cargo.toml new file mode 100644 index 000000000000..c357a4f62d96 --- /dev/null +++ b/compiler/rustc_hir_id/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rustc_hir_id" +version = "0.0.0" +edition = "2024" + +[dependencies] +# tidy-alphabetical-start +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_index = { path = "../rustc_index" } +rustc_macros = { path = "../rustc_macros" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_span = { path = "../rustc_span" } +# tidy-alphabetical-end diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir_id/src/lib.rs similarity index 85% rename from compiler/rustc_hir/src/hir_id.rs rename to compiler/rustc_hir_id/src/lib.rs index b48a081d3714..d07bc88e66af 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir_id/src/lib.rs @@ -1,11 +1,15 @@ +//! Library containing Id types from `rustc_hir`, split out so crates can use it without depending +//! on all of `rustc_hir` (which is large and depends on other large things like `rustc_target`). +#![allow(internal_features)] +#![feature(negative_impls)] +#![feature(rustc_attrs)] + use std::fmt::{self, Debug}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_span::HashStableContext; -use rustc_span::def_id::DefPathHash; - -use crate::def_id::{CRATE_DEF_ID, DefId, DefIndex, LocalDefId}; +pub use rustc_span::HashStableContext; +use rustc_span::def_id::{CRATE_DEF_ID, DefId, DefIndex, DefPathHash, LocalDefId}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] pub struct OwnerId { @@ -171,3 +175,22 @@ impl StableOrd for ItemLocalId { HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO }; pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; + +impl ToStableHashKey for HirId { + type KeyType = (DefPathHash, ItemLocalId); + + #[inline] + fn to_stable_hash_key(&self, hcx: &CTX) -> (DefPathHash, ItemLocalId) { + let def_path_hash = self.owner.def_id.to_stable_hash_key(hcx); + (def_path_hash, self.local_id) + } +} + +impl ToStableHashKey for ItemLocalId { + type KeyType = ItemLocalId; + + #[inline] + fn to_stable_hash_key(&self, _: &CTX) -> ItemLocalId { + *self + } +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 0498a9383663..940f0e3708d0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -290,6 +290,9 @@ pub(super) fn check_expr_with_expectation_and_args( | ExprKind::Let(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {} + // Do not warn on `as` casts from never to any, + // they are sometimes required to appeal typeck. + ExprKind::Cast(_, _) => {} // If `expr` is a result of desugaring the try block and is an ok-wrapped // diverging expression (e.g. it arose from desugaring of `try { return }`), // we skip issuing a warning because it is autogenerated code. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index b80a2af31007..5aec50c8b538 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -83,14 +83,6 @@ pub(in super::super) fn check_casts(&mut self) { *self.deferred_cast_checks.borrow_mut() = deferred_cast_checks; } - pub(in super::super) fn check_transmutes(&self) { - let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut(); - debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len()); - for (from, to, hir_id) in deferred_transmute_checks.drain(..) { - self.check_transmute(from, to, hir_id); - } - } - pub(in super::super) fn check_asms(&self) { let mut deferred_asm_checks = self.deferred_asm_checks.borrow_mut(); debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 194e420b606e..7567f8ba3488 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -7,11 +7,10 @@ use rustc_index::Idx; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::def_id::LocalDefId; use tracing::trace; -use super::FnCtxt; - /// If the type is `Option`, it will return `T`, otherwise /// the type itself. Works on most `Option`-like types. fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { @@ -39,119 +38,117 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { ty } -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// FIXME: Move this check out of typeck, since it'll easily cycle when revealing opaques, - /// and we shouldn't need to check anything here if the typeck results are tainted. - pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) { - let tcx = self.tcx; - let dl = &tcx.data_layout; - let span = tcx.hir_span(hir_id); - let normalize = |ty| { - let ty = self.resolve_vars_if_possible(ty); - if let Ok(ty) = - self.tcx.try_normalize_erasing_regions(self.typing_env(self.param_env), ty) - { - ty +/// Try to display a sensible error with as much information as possible. +fn skeleton_string<'tcx>( + ty: Ty<'tcx>, + sk: Result, &'tcx LayoutError<'tcx>>, +) -> String { + match sk { + Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), + Ok(SizeSkeleton::Known(size, _)) => { + if let Some(v) = u128::from(size.bytes()).checked_mul(8) { + format!("{v} bits") } else { - Ty::new_error_with_message( - tcx, - span, - "tried to normalize non-wf type in check_transmute", - ) + // `u128` should definitely be able to hold the size of different architectures + // larger sizes should be reported as error `are too big for the target architecture` + // otherwise we have a bug somewhere + bug!("{:?} overflow for u128", size) } - }; - let from = normalize(from); - let to = normalize(to); - trace!(?from, ?to); - if from.has_non_region_infer() || to.has_non_region_infer() { - // Note: this path is currently not reached in any test, so any - // example that triggers this would be worth minimizing and - // converting into a test. - self.dcx().span_bug(span, "argument to transmute has inference variables"); } - // Transmutes that are only changing lifetimes are always ok. - if from == to { + Ok(SizeSkeleton::Generic(size)) => { + format!("generic size {size}") + } + Err(LayoutError::TooGeneric(bad)) => { + if *bad == ty { + "this type does not have a fixed size".to_owned() + } else { + format!("size can vary because of {bad}") + } + } + Err(err) => err.to_string(), + } +} + +fn check_transmute<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + from: Ty<'tcx>, + to: Ty<'tcx>, + hir_id: HirId, +) { + let span = || tcx.hir_span(hir_id); + let normalize = |ty| { + if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, ty) { + ty + } else { + Ty::new_error_with_message( + tcx, + span(), + format!("tried to normalize non-wf type {ty:#?} in check_transmute"), + ) + } + }; + + let from = normalize(from); + let to = normalize(to); + trace!(?from, ?to); + + // Transmutes that are only changing lifetimes are always ok. + if from == to { + return; + } + + let sk_from = SizeSkeleton::compute(from, tcx, typing_env); + let sk_to = SizeSkeleton::compute(to, tcx, typing_env); + trace!(?sk_from, ?sk_to); + + // Check for same size using the skeletons. + if let Ok(sk_from) = sk_from + && let Ok(sk_to) = sk_to + { + if sk_from.same_size(sk_to) { return; } - let skel = |ty| SizeSkeleton::compute(ty, tcx, self.typing_env(self.param_env)); - let sk_from = skel(from); - let sk_to = skel(to); - trace!(?sk_from, ?sk_to); - - // Check for same size using the skeletons. - if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) { - if sk_from.same_size(sk_to) { - return; - } - - // Special-case transmuting from `typeof(function)` and - // `Option` to present a clearer error. - let from = unpack_option_like(tcx, from); - if let (&ty::FnDef(..), SizeSkeleton::Known(size_to, _)) = (from.kind(), sk_to) - && size_to == Pointer(dl.instruction_address_space).size(&tcx) - { - struct_span_code_err!(self.dcx(), span, E0591, "can't transmute zero-sized type") - .with_note(format!("source type: {from}")) - .with_note(format!("target type: {to}")) - .with_help("cast with `as` to a pointer instead") - .emit(); - return; - } - } - - // Try to display a sensible error with as much information as possible. - let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk { - Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), - Ok(SizeSkeleton::Known(size, _)) => { - if let Some(v) = u128::from(size.bytes()).checked_mul(8) { - format!("{v} bits") - } else { - // `u128` should definitely be able to hold the size of different architectures - // larger sizes should be reported as error `are too big for the target architecture` - // otherwise we have a bug somewhere - bug!("{:?} overflow for u128", size) - } - } - Ok(SizeSkeleton::Generic(size)) => { - if let Some(size) = - self.try_structurally_resolve_const(span, size).try_to_target_usize(tcx) - { - format!("{size} bytes") - } else { - format!("generic size {size}") - } - } - Err(LayoutError::TooGeneric(bad)) => { - if *bad == ty { - "this type does not have a fixed size".to_owned() - } else { - format!("size can vary because of {bad}") - } - } - Err(err) => err.to_string(), - }; - - let mut err = struct_span_code_err!( - self.dcx(), - span, - E0512, - "cannot transmute between types of different sizes, \ - or dependently-sized types" - ); - if from == to { - err.note(format!("`{from}` does not have a fixed size")); - err.emit(); - } else { - err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) - .note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); - if let Err(LayoutError::ReferencesError(_)) = sk_from { - err.delay_as_bug(); - } else if let Err(LayoutError::ReferencesError(_)) = sk_to { - err.delay_as_bug(); - } else { - err.emit(); - } + // Special-case transmuting from `typeof(function)` and + // `Option` to present a clearer error. + let from = unpack_option_like(tcx, from); + if let ty::FnDef(..) = from.kind() + && let SizeSkeleton::Known(size_to, _) = sk_to + && size_to == Pointer(tcx.data_layout.instruction_address_space).size(&tcx) + { + struct_span_code_err!(tcx.sess.dcx(), span(), E0591, "can't transmute zero-sized type") + .with_note(format!("source type: {from}")) + .with_note(format!("target type: {to}")) + .with_help("cast with `as` to a pointer instead") + .emit(); + return; } } + + let mut err = struct_span_code_err!( + tcx.sess.dcx(), + span(), + E0512, + "cannot transmute between types of different sizes, or dependently-sized types" + ); + if from == to { + err.note(format!("`{from}` does not have a fixed size")); + err.emit(); + } else { + err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))); + err.note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); + err.emit(); + } +} + +pub(crate) fn check_transmutes(tcx: TyCtxt<'_>, owner: LocalDefId) { + assert!(!tcx.is_typeck_child(owner.to_def_id())); + let typeck_results = tcx.typeck(owner); + let None = typeck_results.tainted_by_errors else { return }; + + let typing_env = ty::TypingEnv::post_analysis(tcx, owner); + for &(from, to, hir_id) in &typeck_results.transmutes_to_check { + check_transmute(tcx, typing_env, from, to, hir_id); + } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index aae870f7ee3e..634647a595a1 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,7 +53,7 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::Span; @@ -251,29 +251,10 @@ fn typeck_with_inspect<'tcx>( fcx.report_ambiguity_errors(); } - if let None = fcx.infcx.tainted_by_errors() { - fcx.check_transmutes(); - } - fcx.check_asms(); let typeck_results = fcx.resolve_type_vars_in_body(body); - // Handle potentially region dependent goals, see `InferCtxt::in_hir_typeck`. - if let None = fcx.infcx.tainted_by_errors() { - for obligation in fcx.take_hir_typeck_potentially_region_dependent_goals() { - let obligation = fcx.resolve_vars_if_possible(obligation); - if obligation.has_non_region_infer() { - bug!("unexpected inference variable after writeback: {obligation:?}"); - } - fcx.register_predicate(obligation); - } - fcx.select_obligations_where_possible(|_| {}); - if let None = fcx.infcx.tainted_by_errors() { - fcx.report_ambiguity_errors(); - } - } - fcx.detect_opaque_types_added_during_writeback(); // Consistency check our TypeckResults instance can hold all ItemLocalIds @@ -555,6 +536,7 @@ pub fn provide(providers: &mut Providers) { method_autoderef_steps: method::probe::method_autoderef_steps, typeck, used_trait_imports, + check_transmutes: intrinsicck::check_transmutes, ..*providers }; } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3ca3b56b87e0..f5c3ebd83758 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -909,6 +909,10 @@ fn assemble_inherent_candidates_from_param(&mut self, param_ty: Ty<'tcx>) { debug_assert_matches!(param_ty.kind(), ty::Param(_)); let tcx = self.tcx; + + // We use `DeepRejectCtxt` here which may return false positive on where clauses + // with alias self types. We need to later on reject these as inherent candidates + // in `consider_probe`. let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { @@ -1945,6 +1949,29 @@ fn consider_probe( ); (xform_self_ty, xform_ret_ty) = self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args); + + if matches!(probe.kind, WhereClauseCandidate(_)) { + // `WhereClauseCandidate` requires that the self type is a param, + // because it has special behavior with candidate preference as an + // inherent pick. + match ocx.structurally_normalize_ty( + cause, + self.param_env, + trait_ref.self_ty(), + ) { + Ok(ty) => { + if !matches!(ty.kind(), ty::Param(_)) { + debug!("--> not a param ty: {xform_self_ty:?}"); + return ProbeResult::NoMatch; + } + } + Err(errors) => { + debug!("--> cannot relate self-types {:?}", errors); + return ProbeResult::NoMatch; + } + } + } + xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); match ocx.relate(cause, self.param_env, self.variance(), self_ty, xform_self_ty) { diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 824d592fa6c7..c8f6c06b720d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -13,9 +13,7 @@ use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; -use rustc_errors::{ - Applicability, Diag, DiagStyledString, MultiSpan, StashKey, pluralize, struct_span_code_err, -}; +use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -1560,11 +1558,7 @@ fn report_no_match_method_error( ); } - if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() - || restrict_type_params - || suggested_derive - || self.lookup_alternative_tuple_impls(&mut err, &unsatisfied_predicates) - { + if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive { } else { self.suggest_traits_to_import( &mut err, @@ -1741,119 +1735,6 @@ fn report_no_match_method_error( err.emit() } - /// If the predicate failure is caused by an unmet bound on a tuple, recheck if the bound would - /// succeed if all the types on the tuple had no borrows. This is a common problem for libraries - /// like Bevy and ORMs, which rely heavily on traits being implemented on tuples. - fn lookup_alternative_tuple_impls( - &self, - err: &mut Diag<'_>, - unsatisfied_predicates: &[( - ty::Predicate<'tcx>, - Option>, - Option>, - )], - ) -> bool { - let mut found_tuple = false; - for (pred, root, _ob) in unsatisfied_predicates { - let mut preds = vec![pred]; - if let Some(root) = root { - // We will look at both the current predicate and the root predicate that caused it - // to be needed. If calling something like `<(A, &B)>::default()`, then `pred` is - // `&B: Default` and `root` is `(A, &B): Default`, which is the one we are checking - // for further down, so we check both. - preds.push(root); - } - for pred in preds { - if let Some(clause) = pred.as_clause() - && let Some(clause) = clause.as_trait_clause() - && let ty = clause.self_ty().skip_binder() - && let ty::Tuple(types) = ty.kind() - { - let path = clause.skip_binder().trait_ref.print_only_trait_path(); - let def_id = clause.def_id(); - let ty = Ty::new_tup( - self.tcx, - self.tcx.mk_type_list_from_iter(types.iter().map(|ty| ty.peel_refs())), - ); - let args = ty::GenericArgs::for_item(self.tcx, def_id, |param, _| { - if param.index == 0 { - ty.into() - } else { - self.infcx.var_for_def(DUMMY_SP, param) - } - }); - if self - .infcx - .type_implements_trait(def_id, args, self.param_env) - .must_apply_modulo_regions() - { - // "`Trait` is implemented for `(A, B)` but not for `(A, &B)`" - let mut msg = DiagStyledString::normal(format!("`{path}` ")); - msg.push_highlighted("is"); - msg.push_normal(" implemented for `("); - let len = types.len(); - for (i, t) in types.iter().enumerate() { - msg.push( - format!("{}", with_forced_trimmed_paths!(t.peel_refs())), - t.peel_refs() != t, - ); - if i < len - 1 { - msg.push_normal(", "); - } - } - msg.push_normal(")` but "); - msg.push_highlighted("not"); - msg.push_normal(" for `("); - for (i, t) in types.iter().enumerate() { - msg.push( - format!("{}", with_forced_trimmed_paths!(t)), - t.peel_refs() != t, - ); - if i < len - 1 { - msg.push_normal(", "); - } - } - msg.push_normal(")`"); - - // Find the span corresponding to the impl that was found to point at it. - if let Some(impl_span) = self - .tcx - .all_impls(def_id) - .filter(|&impl_def_id| { - let header = self.tcx.impl_trait_header(impl_def_id).unwrap(); - let trait_ref = header.trait_ref.instantiate( - self.tcx, - self.infcx.fresh_args_for_item(DUMMY_SP, impl_def_id), - ); - - let value = ty::fold_regions(self.tcx, ty, |_, _| { - self.tcx.lifetimes.re_erased - }); - // FIXME: Don't bother dealing with non-lifetime binders here... - if value.has_escaping_bound_vars() { - return false; - } - self.infcx.can_eq(ty::ParamEnv::empty(), trait_ref.self_ty(), value) - && header.polarity == ty::ImplPolarity::Positive - }) - .map(|impl_def_id| self.tcx.def_span(impl_def_id)) - .next() - { - err.highlighted_span_note(impl_span, msg.0); - } else { - err.highlighted_note(msg.0); - } - found_tuple = true; - } - // If `pred` was already on the tuple, we don't need to look at the root - // obligation too. - break; - } - } - } - found_tuple - } - /// If an appropriate error source is not found, check method chain for possible candidates fn lookup_segments_chain_for_no_match_method( &self, diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 093de950d636..131c687bddc6 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -74,7 +74,9 @@ pub(crate) fn resolve_type_vars_in_body( wbcx.visit_user_provided_tys(); wbcx.visit_user_provided_sigs(); wbcx.visit_coroutine_interior(); + wbcx.visit_transmutes(); wbcx.visit_offset_of_container_types(); + wbcx.visit_potentially_region_dependent_goals(); wbcx.typeck_results.rvalue_scopes = mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes); @@ -532,6 +534,18 @@ fn visit_coroutine_interior(&mut self) { } } + fn visit_transmutes(&mut self) { + let tcx = self.tcx(); + let fcx_typeck_results = self.fcx.typeck_results.borrow(); + assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); + for &(from, to, hir_id) in self.fcx.deferred_transmute_checks.borrow().iter() { + let span = tcx.hir_span(hir_id); + let from = self.resolve(from, &span); + let to = self.resolve(to, &span); + self.typeck_results.transmutes_to_check.push((from, to, hir_id)); + } + } + #[instrument(skip(self), level = "debug")] fn visit_opaque_types(&mut self) { let tcx = self.tcx(); @@ -762,6 +776,32 @@ fn visit_offset_of_container_types(&mut self) { } } + fn visit_potentially_region_dependent_goals(&mut self) { + let obligations = self.fcx.take_hir_typeck_potentially_region_dependent_goals(); + if let None = self.fcx.tainted_by_errors() { + for obligation in obligations { + let (predicate, mut cause) = + self.fcx.resolve_vars_if_possible((obligation.predicate, obligation.cause)); + if predicate.has_non_region_infer() { + self.fcx.dcx().span_delayed_bug( + cause.span, + format!("unexpected inference variable after writeback: {predicate:?}"), + ); + } else { + let predicate = self.tcx().erase_regions(predicate); + if cause.has_infer() || cause.has_placeholders() { + // We can't use the the obligation cause as it references + // information local to this query. + cause = self.fcx.misc(cause.span); + } + self.typeck_results + .potentially_region_dependent_goals + .insert((predicate, cause)); + } + } + } + } + fn resolve(&mut self, value: T, span: &dyn Locatable) -> T where T: TypeFoldable>, diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 21e999b080d5..bb9c88500936 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -22,10 +22,6 @@ fn next_trait_solver(&self) -> bool { self.next_trait_solver } - fn in_hir_typeck(&self) -> bool { - self.in_hir_typeck - } - fn typing_mode(&self) -> ty::TypingMode<'tcx> { self.typing_mode() } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 82d4856df39c..d1507f08c061 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -158,7 +158,8 @@ pub struct InferCtxtInner<'tcx> { region_assumptions: Vec>, /// `-Znext-solver`: Successfully proven goals during HIR typeck which - /// reference inference variables and get reproven after writeback. + /// reference inference variables and get reproven in case MIR type check + /// fails to prove something. /// /// See the documentation of `InferCtxt::in_hir_typeck` for more details. hir_typeck_potentially_region_dependent_goals: Vec>, @@ -782,22 +783,30 @@ pub fn num_ty_vars(&self) -> usize { self.inner.borrow_mut().type_variables().num_vars() } + pub fn next_ty_vid(&self, span: Span) -> TyVid { + self.next_ty_vid_with_origin(TypeVariableOrigin { span, param_def_id: None }) + } + + pub fn next_ty_vid_with_origin(&self, origin: TypeVariableOrigin) -> TyVid { + self.inner.borrow_mut().type_variables().new_var(self.universe(), origin) + } + + pub fn next_ty_vid_in_universe(&self, span: Span, universe: ty::UniverseIndex) -> TyVid { + let origin = TypeVariableOrigin { span, param_def_id: None }; + self.inner.borrow_mut().type_variables().new_var(universe, origin) + } + pub fn next_ty_var(&self, span: Span) -> Ty<'tcx> { self.next_ty_var_with_origin(TypeVariableOrigin { span, param_def_id: None }) } pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { - let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin); + let vid = self.next_ty_vid_with_origin(origin); Ty::new_var(self.tcx, vid) } - pub fn next_ty_var_id_in_universe(&self, span: Span, universe: ty::UniverseIndex) -> TyVid { - let origin = TypeVariableOrigin { span, param_def_id: None }; - self.inner.borrow_mut().type_variables().new_var(universe, origin) - } - pub fn next_ty_var_in_universe(&self, span: Span, universe: ty::UniverseIndex) -> Ty<'tcx> { - let vid = self.next_ty_var_id_in_universe(span, universe); + let vid = self.next_ty_vid_in_universe(span, universe); Ty::new_var(self.tcx, vid) } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index c46e879b976a..8f131f45bbdd 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -285,7 +285,9 @@ macro_rules! error { .expecteds .entry(name.name) .and_modify(|v| match v { - ExpectedValues::Some(v) if !values_any_specified => { + ExpectedValues::Some(v) if !values_any_specified => + { + #[allow(rustc::potential_query_instability)] v.extend(values.clone()) } ExpectedValues::Some(_) => *v = ExpectedValues::Any, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 3ba224723e36..bc5ef04079ed 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -6,6 +6,7 @@ use std::{env, fs, iter}; use rustc_ast as ast; +use rustc_attr_parsing::{AttributeParser, ShouldEmit, validate_attr}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::steal::Steal; @@ -15,6 +16,7 @@ use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_incremental::setup_dep_graph; @@ -25,9 +27,7 @@ use rustc_middle::dep_graph::DepsType; use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; -use rustc_parse::{ - new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal, validate_attr, -}; +use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::{Resolver, ResolverOutputs}; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; @@ -1081,7 +1081,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { if !tcx.is_typeck_child(def_id.to_def_id()) { // Child unsafety and borrowck happens together with the parent tcx.ensure_ok().check_unsafety(def_id); - tcx.ensure_ok().mir_borrowck(def_id) + tcx.ensure_ok().mir_borrowck(def_id); + tcx.ensure_ok().check_transmutes(def_id); } tcx.ensure_ok().has_ffi_unwind_calls(def_id); @@ -1244,8 +1245,7 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol // in all code paths that require the crate name very early on, namely before // macro expansion. - let attr_crate_name = - validate_and_find_value_str_builtin_attr(sym::crate_name, sess, krate_attrs); + let attr_crate_name = parse_crate_name(sess, krate_attrs, ShouldEmit::EarlyFatal); let validate = |name, span| { rustc_session::output::validate_crate_name(sess, name, span); @@ -1283,6 +1283,28 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol sym::rust_out } +pub(crate) fn parse_crate_name( + sess: &Session, + attrs: &[ast::Attribute], + emit_errors: ShouldEmit, +) -> Option<(Symbol, Span)> { + let rustc_hir::Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) = + AttributeParser::parse_limited_should_emit( + sess, + &attrs, + sym::crate_name, + DUMMY_SP, + rustc_ast::node_id::CRATE_NODE_ID, + None, + emit_errors, + )? + else { + unreachable!("crate_name is the only attr we could've parsed here"); + }; + + Some((name, name_span)) +} + fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { // We don't permit macro calls inside of the attribute (e.g., #![recursion_limit = `expand!()`]) // because that would require expanding this while in the middle of expansion, which needs to diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0a764808f953..4425877308a7 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -807,6 +807,7 @@ macro_rules! tracked { tracked!(hint_mostly_unused, true); tracked!(human_readable_cgu_names, true); tracked!(incremental_ignore_spans, true); + tracked!(indirect_branch_cs_prefix, true); tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0ca4fcc66ca5..04006f3e446c 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -5,14 +5,15 @@ use std::{env, thread}; use rustc_ast as ast; +use rustc_attr_parsing::{ShouldEmit, validate_attr}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; +use rustc_errors::LintBuffer; use rustc_metadata::{DylibError, load_symbol_from_dylib}; use rustc_middle::ty::CurrentGcx; -use rustc_parse::validate_attr; use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple}; -use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; +use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::{CRATE_TYPES, categorize_crate_type}; use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; @@ -23,6 +24,7 @@ use tracing::info; use crate::errors; +use crate::passes::parse_crate_name; /// Function pointer type that constructs a new CodegenBackend. type MakeBackendFn = fn() -> Box; @@ -519,11 +521,10 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu sess.dcx().emit_fatal(errors::MultipleOutputTypesToStdout); } - let crate_name = sess - .opts - .crate_name - .clone() - .or_else(|| rustc_attr_parsing::find_crate_name(attrs).map(|n| n.to_string())); + let crate_name = + sess.opts.crate_name.clone().or_else(|| { + parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string()) + }); match sess.io.output_file { None => { diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index e80196ed5679..483cc3e93dc2 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -540,11 +540,11 @@ fn frontmatter(&mut self, has_invalid_preceding_whitespace: bool) -> TokenKind { // whitespace between the opening and the infostring. self.eat_while(|ch| ch != '\n' && is_whitespace(ch)); - // copied from `eat_identifier`, but allows `.` in infostring to allow something like + // copied from `eat_identifier`, but allows `-` and `.` in infostring to allow something like // `---Cargo.toml` as a valid opener if is_id_start(self.first()) { self.bump(); - self.eat_while(|c| is_id_continue(c) || c == '.'); + self.eat_while(|c| is_id_continue(c) || c == '-' || c == '.'); } self.eat_while(|ch| ch != '\n' && is_whitespace(ch)); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index c485e6fc8498..417e5a97069c 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -205,8 +205,6 @@ 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_custom_inner_attribute_unstable = custom inner attributes are unstable - lint_dangling_pointers_from_locals = a dangling pointer will be produced because the local variable `{$local_var_name}` will be dropped .ret_ty = return type of the {$fn_kind} is `{$ret_ty}` .local_var = `{$local_var_name}` is part the {$fn_kind} and will be dropped at the end of the {$fn_kind} @@ -271,10 +269,6 @@ lint_expectation = this lint expectation is unfulfilled 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 - .suggestion = explicitly specify the {$default_abi} ABI - lint_for_loops_over_fallibles = 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 @@ -294,19 +288,6 @@ lint_hidden_glob_reexport = private item shadows public glob re-export 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 -> - [one] an invisible - *[other] invisible - } unicode text flow control {$count -> - [one] codepoint - *[other] 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_remove = if their presence wasn't intentional, you can remove them - .suggestion_escape = if you want to keep them but make them visible in your source code, you can escape them - .no_suggestion_note_escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped} - lint_identifier_non_ascii_char = identifier contains non-ASCII characters lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len -> @@ -431,8 +412,6 @@ 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_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly .label = use a different label that doesn't start with `0` or `1` .help = start numbering with `2` instead @@ -483,6 +462,14 @@ lint_invalid_reference_casting_note_book = for more information, visit + .help_exposed_provenance = for more information about exposed provenance, see + .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance + .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + lint_legacy_derive_helpers = derive helper attribute is used before it is introduced .label = the attribute is introduced here @@ -732,7 +719,7 @@ lint_pattern_in_foreign = patterns aren't allowed in foreign function declaratio 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 +lint_proc_macro_derive_resolution_fallback = cannot find {$ns_descr} `{$ident}` in this scope .label = names from parent modules are not accessible without an explicit import lint_query_instability = using `{$query}` can result in unstable query results @@ -870,10 +857,6 @@ 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_builtin_cfg = unexpected `--cfg {$cfg}` flag - .controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}` - .incoherent = manually setting a built-in cfg can and does create incoherent behaviors - 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} diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8006cfcf30fd..c3c0a34df710 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -3097,7 +3097,7 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) { if let ast::ItemKind::Mod( _, ident, - ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _, _), + ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No { .. }, _), ) = item.kind { if item.attrs.iter().any(|a| a.has_name(sym::path)) { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index cb159a0b9143..0669da1a025b 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; +use rustc_errors::{Diag, LintBuffer, LintDiagnostic, MultiSpan}; use rustc_feature::Features; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; @@ -23,8 +23,8 @@ use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths}; use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode}; -use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId}; -use rustc_session::{LintStoreMarker, Session}; +use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId}; +use rustc_session::{DynLintStore, Session}; use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, sym}; use tracing::debug; @@ -62,7 +62,13 @@ pub struct LintStore { lint_groups: FxIndexMap<&'static str, LintGroup>, } -impl LintStoreMarker for LintStore {} +impl DynLintStore for LintStore { + fn lint_groups_iter(&self) -> Box + '_> { + Box::new(self.get_lint_groups().map(|(name, lints, is_externally_loaded)| { + rustc_session::LintGroup { name, lints, is_externally_loaded } + })) + } +} /// The target of the `by_name` map, which accounts for renaming/deprecation. #[derive(Debug)] diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 58205087defe..dff1fc436702 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -7,10 +7,11 @@ use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; use rustc_ast::{self as ast, HasAttrs}; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; use rustc_feature::Features; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; -use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; +use rustc_session::lint::LintPass; use rustc_span::{Ident, Span}; use tracing::debug; @@ -36,8 +37,11 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; - self.context.opt_span_lint(lint_id.lint, span, |diag| { - diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, diagnostic, diag); + self.context.opt_span_lint(lint_id.lint, span, |diag| match diagnostic { + DecorateDiagCompat::Builtin(b) => { + diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, b, diag); + } + DecorateDiagCompat::Dynamic(d) => d.decorate_lint_box(diag), }); } } diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 678d3d1f8ed0..7300490b838b 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -64,10 +64,12 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => { - lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } - .decorate_lint(diag) - } + BuiltinLintDiag::ProcMacroDeriveResolutionFallback { + span: macro_span, + ns_descr, + ident, + } => lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns_descr, ident } + .decorate_lint(diag), BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def } .decorate_lint(diag) @@ -156,9 +158,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::MissingAbi(label_span, default_abi) => { - lints::MissingAbi { span: label_span, default_abi }.decorate_lint(diag); - } BuiltinLintDiag::LegacyDeriveHelpers(label_span) => { lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag); } @@ -184,27 +183,6 @@ pub fn decorate_builtin_lint( lints::ReservedMultihash { suggestion }.decorate_lint(diag); } } - BuiltinLintDiag::HiddenUnicodeCodepoints { - label, - count, - span_label, - labels, - escape, - spans, - } => { - lints::HiddenUnicodeCodepointsDiag { - label: &label, - count, - span_label, - labels: labels.map(|spans| lints::HiddenUnicodeCodepointsDiagLabels { spans }), - sub: if escape { - lints::HiddenUnicodeCodepointsDiagSub::Escape { spans } - } else { - lints::HiddenUnicodeCodepointsDiagSub::NoEscape { spans } - }, - } - .decorate_lint(diag); - } BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, @@ -464,17 +442,8 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag) } - BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro { - lints::InnerAttributeUnstable::InnerMacroAttribute - } else { - lints::InnerAttributeUnstable::CustomInnerAttribute - } - .decorate_lint(diag), BuiltinLintDiag::OutOfScopeMacroCalls { span, path, location } => { lints::OutOfScopeMacroCalls { span, path, location }.decorate_lint(diag) } - BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => { - lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag) - } } } diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 759e6c927b82..3267e70f1de2 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -179,7 +179,7 @@ fn check_foreign_item<'tcx>(&mut self, tcx: TyCtxt<'tcx>, this_fi: hir::ForeignI /// symbol's name. fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName { if let Some((overridden_link_name, overridden_link_name_span)) = - tcx.codegen_fn_attrs(fi).link_name.map(|overridden_link_name| { + tcx.codegen_fn_attrs(fi).symbol_name.map(|overridden_link_name| { // FIXME: Instead of searching through the attributes again to get span // information, we could have codegen_fn_attrs also give span information back for // where the attribute was defined. However, until this is found to be a diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 016ff17f5d7e..929fc8207b03 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -1,10 +1,10 @@ //! Some lints that are only useful in the compiler or crates that use compiler internals, such as //! Clippy. -use rustc_hir::HirId; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; +use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_middle::ty::{self, GenericArgsRef, PredicatePolarity, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::{Span, sym}; @@ -56,25 +56,6 @@ fn check_path(&mut self, cx: &LateContext<'_>, path: &hir::Path<'_>, hir_id: Hir } } -/// Helper function for lints that check for expressions with calls and use typeck results to -/// get the `DefId` and `GenericArgsRef` of the function. -fn typeck_results_of_method_fn<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, -) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> { - match expr.kind { - hir::ExprKind::MethodCall(segment, ..) - if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => - { - Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id))) - } - _ => match cx.typeck_results().node_type(expr.hir_id).kind() { - &ty::FnDef(def_id, args) => Some((expr.span, def_id, args)), - _ => None, - }, - } -} - declare_tool_lint! { /// The `potential_query_instability` lint detects use of methods which can lead to /// potential query instability, such as iterating over a `HashMap`. @@ -101,10 +82,12 @@ fn typeck_results_of_method_fn<'tcx>( declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]); -impl LateLintPass<'_> for QueryStability { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return }; - if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args) +impl<'tcx> LateLintPass<'tcx> for QueryStability { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let Some((callee_def_id, span, generic_args, _recv, _args)) = + get_callee_span_generic_args_and_args(cx, expr) + && let Ok(Some(instance)) = + ty::Instance::try_resolve(cx.tcx, cx.typing_env(), callee_def_id, generic_args) { let def_id = instance.def_id(); if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) { @@ -113,7 +96,15 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { span, QueryInstability { query: cx.tcx.item_name(def_id) }, ); + } else if has_unstable_into_iter_predicate(cx, callee_def_id, generic_args) { + let call_span = span.with_hi(expr.span.hi()); + cx.emit_span_lint( + POTENTIAL_QUERY_INSTABILITY, + call_span, + QueryInstability { query: sym::into_iter }, + ); } + if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) { cx.emit_span_lint( UNTRACKED_QUERY_INFORMATION, @@ -125,6 +116,69 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { } } +fn has_unstable_into_iter_predicate<'tcx>( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + generic_args: GenericArgsRef<'tcx>, +) -> bool { + let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else { + return false; + }; + let Some(into_iter_fn_def_id) = cx.tcx.lang_items().into_iter_fn() else { + return false; + }; + let predicates = cx.tcx.predicates_of(callee_def_id).instantiate(cx.tcx, generic_args); + for (predicate, _) in predicates { + let Some(trait_pred) = predicate.as_trait_clause() else { + continue; + }; + if trait_pred.def_id() != into_iterator_def_id + || trait_pred.polarity() != PredicatePolarity::Positive + { + continue; + } + // `IntoIterator::into_iter` has no additional method args. + let into_iter_fn_args = + cx.tcx.instantiate_bound_regions_with_erased(trait_pred).trait_ref.args; + let Ok(Some(instance)) = ty::Instance::try_resolve( + cx.tcx, + cx.typing_env(), + into_iter_fn_def_id, + into_iter_fn_args, + ) else { + continue; + }; + // Does the input type's `IntoIterator` implementation have the + // `rustc_lint_query_instability` attribute on its `into_iter` method? + if cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_query_instability) { + return true; + } + } + false +} + +/// Checks whether an expression is a function or method call and, if so, returns its `DefId`, +/// `Span`, `GenericArgs`, and arguments. This is a slight augmentation of a similarly named Clippy +/// function, `get_callee_generic_args_and_args`. +fn get_callee_span_generic_args_and_args<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option<(DefId, Span, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> { + if let ExprKind::Call(callee, args) = expr.kind + && let callee_ty = cx.typeck_results().expr_ty(callee) + && let ty::FnDef(callee_def_id, generic_args) = callee_ty.kind() + { + return Some((*callee_def_id, callee.span, generic_args, None, args)); + } + if let ExprKind::MethodCall(segment, recv, args, _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + { + let generic_args = cx.typeck_results().node_args(expr.hir_id); + return Some((method_def_id, segment.ident.span, generic_args, Some(recv), args)); + } + None +} + declare_tool_lint! { /// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::`, /// where `ty::` would suffice. @@ -461,33 +515,22 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]); impl LateLintPass<'_> for Diagnostics { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| { let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra)); result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span))); result }; // Only check function calls and method calls. - let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind { - hir::ExprKind::Call(callee, args) => { - match cx.typeck_results().node_type(callee.hir_id).kind() { - &ty::FnDef(def_id, fn_gen_args) => { - (callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false)) - } - _ => return, // occurs for fns passed as args - } - } - hir::ExprKind::MethodCall(_segment, _recv, args, _span) => { - let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr) - else { - return; - }; - let mut args = collect_args_tys_and_spans(args, true); - args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self` - (span, def_id, fn_gen_args, args) - } - _ => return, + let Some((def_id, span, fn_gen_args, recv, args)) = + get_callee_span_generic_args_and_args(cx, expr) + else { + return; }; + let mut arg_tys_and_spans = collect_args_tys_and_spans(args, recv.is_some()); + if let Some(recv) = recv { + arg_tys_and_spans.insert(0, (cx.tcx.types.self_param, recv.span)); // dummy inserted for `self` + } Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args); Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans); @@ -496,7 +539,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { impl Diagnostics { // Is the type `{D,Subd}iagMessage`? - fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool { + fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: Ty<'cx>) -> bool { if let Some(adt_def) = ty.ty_adt_def() && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did()) && matches!(name, sym::DiagMessage | sym::SubdiagMessage) @@ -510,7 +553,7 @@ fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool { fn untranslatable_diagnostic<'cx>( cx: &LateContext<'cx>, def_id: DefId, - arg_tys_and_spans: &[(MiddleTy<'cx>, Span)], + arg_tys_and_spans: &[(Ty<'cx>, Span)], ) { let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates; diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index f06757b3c237..bdbac7fc4d13 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -133,10 +133,9 @@ pub use late::{check_crate, late_lint_mod, unerased_lint_store}; pub use levels::LintLevelsBuilder; pub use passes::{EarlyLintPass, LateLintPass}; +pub use rustc_errors::BufferedEarlyLint; pub use rustc_session::lint::Level::{self, *}; -pub use rustc_session::lint::{ - BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec, -}; +pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index 464f4fc34b96..413525eb6e51 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -214,9 +214,9 @@ pub fn len(&self) -> LifetimeSyntaxCategories { } } - pub fn flatten(&self) -> impl Iterator { - let Self { hidden, elided, named } = self; - [hidden.iter(), elided.iter(), named.iter()].into_iter().flatten() + pub fn iter_unnamed(&self) -> impl Iterator { + let Self { hidden, elided, named: _ } = self; + [hidden.iter(), elided.iter()].into_iter().flatten() } } @@ -495,7 +495,7 @@ fn emit_mismatch_diagnostic<'tcx>( cx.emit_span_lint( MISMATCHED_LIFETIME_SYNTAXES, - inputs.flatten().copied().collect::>(), + inputs.iter_unnamed().chain(outputs.iter_unnamed()).copied().collect::>(), lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions }, ); } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index a6d59af69006..426500dda4a7 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1,14 +1,14 @@ +// ignore-tidy-filelength + #![allow(rustc::untranslatable_diagnostic)] use std::num::NonZero; -use rustc_abi::ExternAbi; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, Subdiagnostic, SuggestionStyle, }; use rustc_hir as hir; -use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::VisitorExt; use rustc_macros::{LintDiagnostic, Subdiagnostic}; @@ -817,80 +817,6 @@ pub(crate) enum InvalidReferenceCastingDiag<'tcx> { }, } -// hidden_unicode_codepoints.rs -#[derive(LintDiagnostic)] -#[diag(lint_hidden_unicode_codepoints)] -#[note] -pub(crate) struct HiddenUnicodeCodepointsDiag<'a> { - pub label: &'a str, - pub count: usize, - #[label] - pub span_label: Span, - #[subdiagnostic] - pub labels: Option, - #[subdiagnostic] - pub sub: HiddenUnicodeCodepointsDiagSub, -} - -pub(crate) struct HiddenUnicodeCodepointsDiagLabels { - pub spans: Vec<(char, Span)>, -} - -impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - for (c, span) in self.spans { - diag.span_label(span, format!("{c:?}")); - } - } -} - -pub(crate) enum HiddenUnicodeCodepointsDiagSub { - Escape { spans: Vec<(char, Span)> }, - NoEscape { spans: Vec<(char, Span)> }, -} - -// Used because of multiple multipart_suggestion and note -impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - match self { - HiddenUnicodeCodepointsDiagSub::Escape { spans } => { - diag.multipart_suggestion_with_style( - fluent::lint_suggestion_remove, - spans.iter().map(|(_, span)| (*span, "".to_string())).collect(), - Applicability::MachineApplicable, - SuggestionStyle::HideCodeAlways, - ); - diag.multipart_suggestion( - fluent::lint_suggestion_escape, - spans - .into_iter() - .map(|(c, span)| { - let c = format!("{c:?}"); - (span, c[1..c.len() - 1].to_string()) - }) - .collect(), - Applicability::MachineApplicable, - ); - } - HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => { - // FIXME: in other suggestions we've reversed the inner spans of doc comments. We - // should do the same here to provide the same good suggestions as we do for - // literals above. - diag.arg( - "escaped", - spans - .into_iter() - .map(|(c, _)| format!("{c:?}")) - .collect::>() - .join(", "), - ); - diag.note(fluent::lint_suggestion_remove); - diag.note(fluent::lint_no_suggestion_note_escape); - } - } - } -} - // map_unit_fn.rs #[derive(LintDiagnostic)] #[diag(lint_map_unit_fn)] @@ -1618,6 +1544,48 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { } } +// transmute.rs +#[derive(LintDiagnostic)] +#[diag(lint_int_to_ptr_transmutes)] +#[note] +#[note(lint_note_exposed_provenance)] +#[help(lint_suggestion_without_provenance_mut)] +#[help(lint_help_transmute)] +#[help(lint_help_exposed_provenance)] +pub(crate) struct IntegerToPtrTransmutes<'tcx> { + #[subdiagnostic] + pub suggestion: IntegerToPtrTransmutesSuggestion<'tcx>, +} + +#[derive(Subdiagnostic)] +pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> { + #[multipart_suggestion( + lint_suggestion_with_exposed_provenance, + applicability = "machine-applicable", + style = "verbose" + )] + ToPtr { + dst: Ty<'tcx>, + suffix: &'static str, + #[suggestion_part(code = "std::ptr::with_exposed_provenance{suffix}::<{dst}>(")] + start_call: Span, + }, + #[multipart_suggestion( + lint_suggestion_with_exposed_provenance, + applicability = "machine-applicable", + style = "verbose" + )] + ToRef { + dst: Ty<'tcx>, + suffix: &'static str, + ref_mutbl: &'static str, + #[suggestion_part( + code = "&{ref_mutbl}*std::ptr::with_exposed_provenance{suffix}::<{dst}>(" + )] + start_call: Span, + }, +} + // types.rs #[derive(LintDiagnostic)] #[diag(lint_range_endpoint_out_of_range)] @@ -2567,16 +2535,6 @@ pub(crate) enum CargoHelp { } } -#[derive(LintDiagnostic)] -#[diag(lint_unexpected_builtin_cfg)] -#[note(lint_controlled_by)] -#[note(lint_incoherent)] -pub(crate) struct UnexpectedBuiltinCfg { - pub(crate) cfg: String, - pub(crate) cfg_name: Symbol, - pub(crate) controlled_by: &'static str, -} - #[derive(LintDiagnostic)] #[diag(lint_macro_use_deprecated)] #[help] @@ -2690,14 +2648,6 @@ pub(crate) struct IllFormedAttributeInput { pub docs: &'static str, } -#[derive(LintDiagnostic)] -pub(crate) enum InnerAttributeUnstable { - #[diag(lint_inner_macro_attribute_unstable)] - InnerMacroAttribute, - #[diag(lint_custom_inner_attribute_unstable)] - CustomInnerAttribute, -} - #[derive(LintDiagnostic)] #[diag(lint_unknown_diagnostic_attribute)] pub(crate) struct UnknownDiagnosticAttribute { @@ -2769,7 +2719,7 @@ pub(crate) struct AbsPathWithModuleSugg { pub(crate) struct ProcMacroDeriveResolutionFallback { #[label] pub span: Span, - pub ns: Namespace, + pub ns_descr: &'static str, pub ident: Ident, } @@ -2890,14 +2840,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub { pub ident: Ident, } -#[derive(LintDiagnostic)] -#[diag(lint_extern_without_abi)] -pub(crate) struct MissingAbi { - #[suggestion(code = "extern {default_abi}", applicability = "machine-applicable")] - pub span: Span, - pub default_abi: ExternAbi, -} - #[derive(LintDiagnostic)] #[diag(lint_legacy_derive_helpers)] pub(crate) struct LegacyDeriveHelpers { diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 8fafaa33d0c4..65075cfecfa4 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -5,7 +5,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor}; -use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind, find_attr}; +use rustc_hir::{Attribute, GenericParamKind, PatExprKind, PatKind, find_attr}; use rustc_middle::hir::nested_filter::All; use rustc_middle::ty; use rustc_session::config::CrateType; @@ -343,35 +343,27 @@ fn check_mod(&mut self, cx: &LateContext<'_>, _: &'tcx hir::Mod<'tcx>, id: hir:: let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name { Some(Ident::from_str(name)) } else { - ast::attr::find_by_name(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), sym::crate_name).and_then( - |attr| { - if let Attribute::Unparsed(n) = attr - && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } = - n.as_ref() - && let ast::LitKind::Str(name, ..) = lit.kind - { - // Discard the double quotes surrounding the literal. - let sp = cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .ok() - .and_then(|snippet| { - let left = snippet.find('"')?; - let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?; + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::CrateName{name, name_span,..} => (name, name_span)).map( + |(&name, &span)| { + // Discard the double quotes surrounding the literal. + let sp = cx + .sess() + .source_map() + .span_to_snippet(span) + .ok() + .and_then(|snippet| { + let left = snippet.find('"')?; + let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?; - Some( - lit.span - .with_lo(lit.span.lo() + BytePos(left as u32 + 1)) - .with_hi(lit.span.hi() - BytePos(right as u32)), - ) - }) - .unwrap_or(lit.span); + Some( + span + .with_lo(span.lo() + BytePos(left as u32 + 1)) + .with_hi(span.hi() - BytePos(right as u32)), + ) + }) + .unwrap_or(span); - Some(Ident::new(name, sp)) - } else { - None - } + Ident::new(name, sp) }, ) }; diff --git a/compiler/rustc_lint/src/transmute.rs b/compiler/rustc_lint/src/transmute.rs index bc1d4587d076..239c86490416 100644 --- a/compiler/rustc_lint/src/transmute.rs +++ b/compiler/rustc_lint/src/transmute.rs @@ -1,3 +1,4 @@ +use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LocalDefId; @@ -7,6 +8,7 @@ use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::sym; +use crate::lints::{IntegerToPtrTransmutes, IntegerToPtrTransmutesSuggestion}; use crate::{LateContext, LateLintPass}; declare_lint! { @@ -67,9 +69,44 @@ "detects transmutes that can also be achieved by other operations" } +declare_lint! { + /// The `integer_to_ptr_transmutes` lint detects integer to pointer + /// transmutes where the resulting pointers are undefined behavior to dereference. + /// + /// ### Example + /// + /// ```rust + /// fn foo(a: usize) -> *const u8 { + /// unsafe { + /// std::mem::transmute::(a) + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Any attempt to use the resulting pointers are undefined behavior as the resulting + /// pointers won't have any provenance. + /// + /// Alternatively, [`std::ptr::with_exposed_provenance`] should be used, as they do not + /// carry the provenance requirement. If wanting to create pointers without provenance + /// [`std::ptr::without_provenance`] should be used instead. + /// + /// See [`std::mem::transmute`] in the reference for more details. + /// + /// [`std::mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html + /// [`std::ptr::with_exposed_provenance`]: https://doc.rust-lang.org/std/ptr/fn.with_exposed_provenance.html + /// [`std::ptr::without_provenance`]: https://doc.rust-lang.org/std/ptr/fn.without_provenance.html + pub INTEGER_TO_PTR_TRANSMUTES, + Warn, + "detects integer to pointer transmutes", +} + pub(crate) struct CheckTransmutes; -impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES]); +impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES, INTEGER_TO_PTR_TRANSMUTES]); impl<'tcx> LateLintPass<'tcx> for CheckTransmutes { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { @@ -94,9 +131,62 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { check_ptr_transmute_in_const(cx, expr, body_owner_def_id, const_context, src, dst); check_unnecessary_transmute(cx, expr, callee, arg, const_context, src, dst); + check_int_to_ptr_transmute(cx, expr, arg, src, dst); } } +/// Check for transmutes from integer to pointers (*const/*mut and &/&mut). +/// +/// Using the resulting pointers would be undefined behavior. +fn check_int_to_ptr_transmute<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + src: Ty<'tcx>, + dst: Ty<'tcx>, +) { + if !matches!(src.kind(), ty::Uint(_) | ty::Int(_)) { + return; + } + let (ty::Ref(_, inner_ty, mutbl) | ty::RawPtr(inner_ty, mutbl)) = dst.kind() else { + return; + }; + // bail-out if the argument is literal 0 as we have other lints for those cases + if matches!(arg.kind, hir::ExprKind::Lit(hir::Lit { node: LitKind::Int(v, _), .. }) if v == 0) { + return; + } + // bail-out if the inner type is a ZST + let Ok(layout_inner_ty) = cx.tcx.layout_of(cx.typing_env().as_query_input(*inner_ty)) else { + return; + }; + if layout_inner_ty.is_1zst() { + return; + } + + let suffix = if mutbl.is_mut() { "_mut" } else { "" }; + cx.tcx.emit_node_span_lint( + INTEGER_TO_PTR_TRANSMUTES, + expr.hir_id, + expr.span, + IntegerToPtrTransmutes { + suggestion: if dst.is_ref() { + IntegerToPtrTransmutesSuggestion::ToRef { + dst: *inner_ty, + suffix, + ref_mutbl: mutbl.prefix_str(), + start_call: expr.span.shrink_to_lo().until(arg.span), + } + } else { + IntegerToPtrTransmutesSuggestion::ToPtr { + dst: *inner_ty, + suffix, + start_call: expr.span.shrink_to_lo().until(arg.span), + } + }, + }, + ); +} + /// Check for transmutes that exhibit undefined behavior. /// For example, transmuting pointers to integers in a const context. /// diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml index 9ab350daf69d..c8201d5ea8cc 100644 --- a/compiler/rustc_lint_defs/Cargo.toml +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -5,11 +5,10 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_messages = { path = "../rustc_error_messages" } -rustc_hir = { path = "../rustc_hir" } +rustc_hir_id = { path = "../rustc_hir_id" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a0083a7df3d2..97aa10659679 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2301,18 +2301,18 @@ declare_lint! { /// The `inline_no_sanitize` lint detects incompatible use of - /// [`#[inline(always)]`][inline] and [`#[no_sanitize(...)]`][no_sanitize]. + /// [`#[inline(always)]`][inline] and [`#[sanitize(xyz = "off")]`][sanitize]. /// /// [inline]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute - /// [no_sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html + /// [sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html /// /// ### Example /// /// ```rust - /// #![feature(no_sanitize)] + /// #![feature(sanitize)] /// /// #[inline(always)] - /// #[no_sanitize(address)] + /// #[sanitize(address = "off")] /// fn x() {} /// /// fn main() { @@ -2325,11 +2325,11 @@ /// ### Explanation /// /// The use of the [`#[inline(always)]`][inline] attribute prevents the - /// the [`#[no_sanitize(...)]`][no_sanitize] attribute from working. + /// the [`#[sanitize(xyz = "off")]`][sanitize] attribute from working. /// Consider temporarily removing `inline` attribute. pub INLINE_NO_SANITIZE, Warn, - "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`", + r#"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"#, } declare_lint! { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 3bb7bbce5675..2e84233e5a56 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,16 +1,15 @@ -use rustc_abi::ExternAbi; +use std::borrow::Cow; + use rustc_ast::AttrId; use rustc_ast::attr::AttributeExt; -use rustc_ast::node_id::NodeId; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; -use rustc_error_messages::{DiagMessage, MultiSpan}; -use rustc_hir::def::Namespace; -use rustc_hir::def_id::DefPathHash; -use rustc_hir::{HashStableContext, HirId, ItemLocalId}; +use rustc_error_messages::{DiagArgValue, DiagMessage, IntoDiagArg, MultiSpan}; +use rustc_hir_id::{HashStableContext, HirId, ItemLocalId}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; use serde::{Deserialize, Serialize}; @@ -138,7 +137,7 @@ pub fn set_lint_index(&mut self, new_lint_index: Option) { } } -impl HashStable for LintExpectationId { +impl HashStable for LintExpectationId { #[inline] fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { match self { @@ -156,7 +155,7 @@ fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { } } -impl ToStableHashKey for LintExpectationId { +impl ToStableHashKey for LintExpectationId { type KeyType = (DefPathHash, ItemLocalId, u16, u16); #[inline] @@ -297,6 +296,12 @@ pub fn is_error(self) -> bool { } } +impl IntoDiagArg for Level { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.to_cmd_flag())) + } +} + /// Specification of a single lint. #[derive(Copy, Clone, Debug)] pub struct Lint { @@ -617,7 +622,7 @@ pub enum BuiltinLintDiag { AbsPathWithModule(Span), ProcMacroDeriveResolutionFallback { span: Span, - ns: Namespace, + ns_descr: &'static str, ident: Ident, }, MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), @@ -641,7 +646,6 @@ pub enum BuiltinLintDiag { path: String, since_kind: DeprecatedSinceKind, }, - MissingAbi(Span, ExternAbi), UnusedDocComment(Span), UnusedBuiltinAttribute { attr_name: Symbol, @@ -664,14 +668,6 @@ pub enum BuiltinLintDiag { is_string: bool, suggestion: Span, }, - HiddenUnicodeCodepoints { - label: String, - count: usize, - span_label: Span, - labels: Option>, - escape: bool, - spans: Vec<(char, Span)>, - }, TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), UnicodeTextFlow(Span, String), @@ -796,68 +792,11 @@ pub enum BuiltinLintDiag { suggestions: Vec, docs: Option<&'static str>, }, - InnerAttributeUnstable { - is_macro: bool, - }, OutOfScopeMacroCalls { span: Span, path: String, location: String, }, - UnexpectedBuiltinCfg { - cfg: String, - cfg_name: Symbol, - controlled_by: &'static str, - }, -} - -/// Lints that are buffered up early on in the `Session` before the -/// `LintLevels` is calculated. -#[derive(Debug)] -pub struct BufferedEarlyLint { - /// The span of code that we are linting on. - pub span: Option, - - /// The `NodeId` of the AST node that generated the lint. - pub node_id: NodeId, - - /// A lint Id that can be passed to - /// `rustc_lint::early::EarlyContextAndPass::check_id`. - pub lint_id: LintId, - - /// Customization of the `Diag<'_>` for the lint. - pub diagnostic: BuiltinLintDiag, -} - -#[derive(Default, Debug)] -pub struct LintBuffer { - pub map: FxIndexMap>, -} - -impl LintBuffer { - pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { - self.map.entry(early_lint.node_id).or_default().push(early_lint); - } - - pub fn take(&mut self, id: NodeId) -> Vec { - // FIXME(#120456) - is `swap_remove` correct? - self.map.swap_remove(&id).unwrap_or_default() - } - - pub fn buffer_lint( - &mut self, - lint: &'static Lint, - node_id: NodeId, - span: impl Into, - diagnostic: BuiltinLintDiag, - ) { - self.add_early_lint(BufferedEarlyLint { - lint_id: LintId::of(lint), - node_id, - span: Some(span.into()), - diagnostic, - }); - } } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 1394edcee6b3..d01f79dcade0 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -226,7 +226,6 @@ fn main() { rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper")); cfg.file("llvm-wrapper/PassWrapper.cpp") .file("llvm-wrapper/RustWrapper.cpp") - .file("llvm-wrapper/ArchiveWrapper.cpp") .file("llvm-wrapper/CoverageMappingWrapper.cpp") .file("llvm-wrapper/SymbolWrapper.cpp") .file("llvm-wrapper/Linker.cpp") diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp deleted file mode 100644 index feac6a5649c8..000000000000 --- a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "LLVMWrapper.h" - -#include "llvm/Object/Archive.h" -#include "llvm/Object/ArchiveWriter.h" -#include "llvm/Support/Path.h" - -using namespace llvm; -using namespace llvm::object; - -struct RustArchiveMember { - const char *Filename; - const char *Name; - Archive::Child Child; - - RustArchiveMember() - : Filename(nullptr), Name(nullptr), Child(nullptr, nullptr, nullptr) {} - ~RustArchiveMember() {} -}; - -struct RustArchiveIterator { - bool First; - Archive::child_iterator Cur; - Archive::child_iterator End; - std::unique_ptr Err; - - RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End, - std::unique_ptr Err) - : First(true), Cur(Cur), End(End), Err(std::move(Err)) {} -}; - -enum class LLVMRustArchiveKind { - GNU, - BSD, - DARWIN, - COFF, - AIX_BIG, -}; - -static Archive::Kind fromRust(LLVMRustArchiveKind Kind) { - switch (Kind) { - case LLVMRustArchiveKind::GNU: - return Archive::K_GNU; - case LLVMRustArchiveKind::BSD: - return Archive::K_BSD; - case LLVMRustArchiveKind::DARWIN: - return Archive::K_DARWIN; - case LLVMRustArchiveKind::COFF: - return Archive::K_COFF; - case LLVMRustArchiveKind::AIX_BIG: - return Archive::K_AIXBIG; - default: - report_fatal_error("Bad ArchiveKind."); - } -} - -typedef OwningBinary *LLVMRustArchiveRef; -typedef RustArchiveMember *LLVMRustArchiveMemberRef; -typedef Archive::Child *LLVMRustArchiveChildRef; -typedef Archive::Child const *LLVMRustArchiveChildConstRef; -typedef RustArchiveIterator *LLVMRustArchiveIteratorRef; - -extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) { - ErrorOr> BufOr = MemoryBuffer::getFile( - Path, /*IsText*/ false, /*RequiresNullTerminator=*/false); - if (!BufOr) { - LLVMRustSetLastError(BufOr.getError().message().c_str()); - return nullptr; - } - - Expected> ArchiveOr = - Archive::create(BufOr.get()->getMemBufferRef()); - - if (!ArchiveOr) { - LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str()); - return nullptr; - } - - OwningBinary *Ret = new OwningBinary( - std::move(ArchiveOr.get()), std::move(BufOr.get())); - - return Ret; -} - -extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) { - delete RustArchive; -} - -extern "C" LLVMRustArchiveIteratorRef -LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) { - Archive *Archive = RustArchive->getBinary(); - std::unique_ptr Err = std::make_unique(Error::success()); - auto Cur = Archive->child_begin(*Err); - if (*Err) { - LLVMRustSetLastError(toString(std::move(*Err)).c_str()); - return nullptr; - } - auto End = Archive->child_end(); - return new RustArchiveIterator(Cur, End, std::move(Err)); -} - -extern "C" LLVMRustArchiveChildConstRef -LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) { - if (RAI->Cur == RAI->End) - return nullptr; - - // Advancing the iterator validates the next child, and this can - // uncover an error. LLVM requires that we check all Errors, - // so we only advance the iterator if we actually need to fetch - // the next child. - // This means we must not advance the iterator in the *first* call, - // but instead advance it *before* fetching the child in all later calls. - if (!RAI->First) { - ++RAI->Cur; - if (*RAI->Err) { - LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str()); - return nullptr; - } - } else { - RAI->First = false; - } - - if (RAI->Cur == RAI->End) - return nullptr; - - const Archive::Child &Child = *RAI->Cur.operator->(); - Archive::Child *Ret = new Archive::Child(Child); - - return Ret; -} - -extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) { - delete Child; -} - -extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) { - delete RAI; -} - -extern "C" const char * -LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) { - Expected NameOrErr = Child->getName(); - if (!NameOrErr) { - // rustc_codegen_llvm currently doesn't use this error string, but it might - // be useful in the future, and in the meantime this tells LLVM that the - // error was not ignored and that it shouldn't abort the process. - LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str()); - return nullptr; - } - StringRef Name = NameOrErr.get(); - *Size = Name.size(); - return Name.data(); -} - -extern "C" LLVMRustArchiveMemberRef -LLVMRustArchiveMemberNew(char *Filename, char *Name, - LLVMRustArchiveChildRef Child) { - RustArchiveMember *Member = new RustArchiveMember; - Member->Filename = Filename; - Member->Name = Name; - if (Child) - Member->Child = *Child; - return Member; -} - -extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) { - delete Member; -} - -extern "C" LLVMRustResult LLVMRustWriteArchive( - char *Dst, size_t NumMembers, const LLVMRustArchiveMemberRef *NewMembers, - bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) { - - std::vector Members; - auto Kind = fromRust(RustKind); - - for (size_t I = 0; I < NumMembers; I++) { - auto Member = NewMembers[I]; - assert(Member->Name); - if (Member->Filename) { - Expected MOrErr = - NewArchiveMember::getFile(Member->Filename, true); - if (!MOrErr) { - LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); - return LLVMRustResult::Failure; - } - MOrErr->MemberName = sys::path::filename(MOrErr->MemberName); - Members.push_back(std::move(*MOrErr)); - } else { - Expected MOrErr = - NewArchiveMember::getOldMember(Member->Child, true); - if (!MOrErr) { - LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); - return LLVMRustResult::Failure; - } - Members.push_back(std::move(*MOrErr)); - } - } - - auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab - : SymtabWritingMode::NoSymtab; - auto Result = - writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC); - if (!Result) - return LLVMRustResult::Success; - LLVMRustSetLastError(toString(std::move(Result)).c_str()); - - return LLVMRustResult::Failure; -} diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index cd4f80f808c6..e699e4b9c13f 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -278,6 +278,7 @@ enum class LLVMRustAttributeKind { Writable = 42, DeadOnUnwind = 43, DeadOnReturn = 44, + CapturesReadOnly = 45, }; static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { @@ -376,6 +377,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { #else report_fatal_error("DeadOnReturn attribute requires LLVM 21 or later"); #endif + case LLVMRustAttributeKind::CapturesReadOnly: + report_fatal_error("Should be handled separately"); } report_fatal_error("bad LLVMRustAttributeKind"); } @@ -430,6 +433,11 @@ LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) { if (RustAttr == LLVMRustAttributeKind::NoCapture) { return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none())); } + if (RustAttr == LLVMRustAttributeKind::CapturesReadOnly) { + return wrap(Attribute::getWithCaptureInfo( + *unwrap(C), CaptureInfo(CaptureComponents::Address | + CaptureComponents::ReadProvenance))); + } #endif return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr))); } @@ -1460,60 +1468,6 @@ LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) { return toRust((DiagnosticKind)unwrap(DI)->getKind()); } -// This is kept distinct from LLVMGetTypeKind, because when -// a new type kind is added, the Rust-side enum must be -// updated or UB will result. -extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { - switch (unwrap(Ty)->getTypeID()) { - case Type::VoidTyID: - return LLVMVoidTypeKind; - case Type::HalfTyID: - return LLVMHalfTypeKind; - case Type::FloatTyID: - return LLVMFloatTypeKind; - case Type::DoubleTyID: - return LLVMDoubleTypeKind; - case Type::X86_FP80TyID: - return LLVMX86_FP80TypeKind; - case Type::FP128TyID: - return LLVMFP128TypeKind; - case Type::PPC_FP128TyID: - return LLVMPPC_FP128TypeKind; - case Type::LabelTyID: - return LLVMLabelTypeKind; - case Type::MetadataTyID: - return LLVMMetadataTypeKind; - case Type::IntegerTyID: - return LLVMIntegerTypeKind; - case Type::FunctionTyID: - return LLVMFunctionTypeKind; - case Type::StructTyID: - return LLVMStructTypeKind; - case Type::ArrayTyID: - return LLVMArrayTypeKind; - case Type::PointerTyID: - return LLVMPointerTypeKind; - case Type::FixedVectorTyID: - return LLVMVectorTypeKind; - case Type::TokenTyID: - return LLVMTokenTypeKind; - case Type::ScalableVectorTyID: - return LLVMScalableVectorTypeKind; - case Type::BFloatTyID: - return LLVMBFloatTypeKind; - case Type::X86_AMXTyID: - return LLVMX86_AMXTypeKind; - default: { - std::string error; - auto stream = llvm::raw_string_ostream(error); - stream << "Rust does not support the TypeID: " << unwrap(Ty)->getTypeID() - << " for the type: " << *unwrap(Ty); - stream.flush(); - report_fatal_error(error.c_str()); - } - } -} - DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef) extern "C" LLVMSMDiagnosticRef LLVMRustGetSMDiagnostic(LLVMDiagnosticInfoRef DI, @@ -1993,29 +1947,3 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) { MD.NoHWAddress = true; GV.setSanitizerMetadata(MD); } - -enum class LLVMRustTailCallKind { - None = 0, - Tail = 1, - MustTail = 2, - NoTail = 3 -}; - -extern "C" void LLVMRustSetTailCallKind(LLVMValueRef Call, - LLVMRustTailCallKind Kind) { - CallInst *CI = unwrap(Call); - switch (Kind) { - case LLVMRustTailCallKind::None: - CI->setTailCallKind(CallInst::TCK_None); - break; - case LLVMRustTailCallKind::Tail: - CI->setTailCallKind(CallInst::TCK_Tail); - break; - case LLVMRustTailCallKind::MustTail: - CI->setTailCallKind(CallInst::TCK_MustTail); - break; - case LLVMRustTailCallKind::NoTail: - CI->setTailCallKind(CallInst::TCK_NoTail); - break; - } -} diff --git a/compiler/rustc_macros/src/print_attribute.rs b/compiler/rustc_macros/src/print_attribute.rs index 9023520c7504..0114e0dfde0d 100644 --- a/compiler/rustc_macros/src/print_attribute.rs +++ b/compiler/rustc_macros/src/print_attribute.rs @@ -4,7 +4,7 @@ use syn::{Data, Fields, Ident}; use synstructure::Structure; -fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, TokenStream) { +fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream) { let string_name = name.to_string(); let mut disps = vec![quote! {let mut __printed_anything = false;}]; @@ -43,7 +43,6 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok #(#disps)* __p.word("}"); }, - quote! { true }, ) } Fields::Unnamed(fields_unnamed) => { @@ -76,10 +75,9 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok #(#disps)* __p.pclose(); }, - quote! { true }, ) } - Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }, quote! { true }), + Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }), } } @@ -89,51 +87,33 @@ pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream { }; // Must be applied to an enum type. - let (code, printed) = match &input.ast().data { + let code = match &input.ast().data { Data::Enum(e) => { - let (arms, printed) = e + let arms = e .variants .iter() .map(|x| { let ident = &x.ident; - let (pat, code, printed) = print_fields(ident, &x.fields); + let (pat, code) = print_fields(ident, &x.fields); - ( - quote! { - Self::#ident #pat => {#code} - }, - quote! { - Self::#ident #pat => {#printed} - }, - ) + quote! { + Self::#ident #pat => {#code} + } }) - .unzip::<_, _, Vec<_>, Vec<_>>(); + .collect::>(); - ( - quote! { - match self { - #(#arms)* - } - }, - quote! { - match self { - #(#printed)* - } - }, - ) + quote! { + match self { + #(#arms)* + } + } } Data::Struct(s) => { - let (pat, code, printed) = print_fields(&input.ast().ident, &s.fields); - ( - quote! { - let Self #pat = self; - #code - }, - quote! { - let Self #pat = self; - #printed - }, - ) + let (pat, code) = print_fields(&input.ast().ident, &s.fields); + quote! { + let Self #pat = self; + #code + } } Data::Union(u) => { return span_error(u.union_token.span(), "can't derive PrintAttribute on unions"); @@ -144,7 +124,7 @@ pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream { input.gen_impl(quote! { #[allow(unused)] gen impl PrintAttribute for @Self { - fn should_render(&self) -> bool { #printed } + fn should_render(&self) -> bool { true } fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code } } }) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 6bfb3769f247..5d776ea581dd 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -32,6 +32,7 @@ use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::validate_crate_name; use rustc_session::search_paths::PathKind; +use rustc_span::def_id::DefId; use rustc_span::edition::Edition; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_target::spec::{PanicStrategy, Target}; @@ -275,6 +276,10 @@ pub(crate) fn iter_crate_data(&self) -> impl Iterator impl Iterator { + self.iter_crate_data().flat_map(|(krate, data)| data.proc_macros_for_crate(krate, self)) + } + fn push_dependencies_in_postorder(&self, deps: &mut IndexSet, cnum: CrateNum) { if !deps.contains(&cnum) { let data = self.get_crate_data(cnum); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 958e314efabb..63f1b51df1c6 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -701,7 +701,7 @@ fn build_dll_import( .link_ordinal .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord))); - let name = codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item)); + let name = codegen_fn_attrs.symbol_name.unwrap_or_else(|| self.tcx.item_name(item)); if self.tcx.sess.target.binary_format == BinaryFormat::Elf { let name = name.as_str(); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 548c56a97bc0..110b26c62ef0 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -2014,6 +2014,22 @@ pub(crate) fn is_proc_macro_crate(&self) -> bool { self.root.is_proc_macro_crate() } + pub(crate) fn proc_macros_for_crate( + &self, + krate: CrateNum, + cstore: &CStore, + ) -> impl Iterator { + gen move { + for def_id in self.root.proc_macro_data.as_ref().into_iter().flat_map(move |data| { + data.macros + .decode(CrateMetadataRef { cdata: self, cstore }) + .map(move |index| DefId { index, krate }) + }) { + yield def_id; + } + } + } + pub(crate) fn name(&self) -> Symbol { self.root.header.name } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index e5cc23c213d2..cf0549fa668a 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -60,6 +60,7 @@ #![feature(try_trait_v2_residual)] #![feature(try_trait_v2_yeet)] #![feature(type_alias_impl_trait)] +#![feature(unwrap_infallible)] #![feature(yeet_expr)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 341a735f88fe..f70b7b6fad7a 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -211,11 +211,28 @@ pub fn new( } fn explain_lint_level_source( + sess: &Session, lint: &'static Lint, level: Level, src: LintLevelSource, err: &mut Diag<'_, ()>, ) { + // Find the name of the lint group that contains the given lint. + // Assumes the lint only belongs to one group. + let lint_group_name = |lint| { + let lint_groups_iter = sess.lint_groups_iter(); + let lint_id = LintId::of(lint); + lint_groups_iter + .filter(|lint_group| !lint_group.is_externally_loaded) + .find(|lint_group| { + lint_group + .lints + .iter() + .find(|lint_group_lint| **lint_group_lint == lint_id) + .is_some() + }) + .map(|lint_group| lint_group.name) + }; let name = lint.name_lower(); if let Level::Allow = level { // Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint @@ -224,7 +241,15 @@ fn explain_lint_level_source( } match src { LintLevelSource::Default => { - err.note_once(format!("`#[{}({})]` on by default", level.as_str(), name)); + let level_str = level.as_str(); + match lint_group_name(lint) { + Some(group_name) => { + err.note_once(format!("`#[{level_str}({name})]` (part of `#[{level_str}({group_name})]`) on by default")); + } + None => { + err.note_once(format!("`#[{level_str}({name})]` on by default")); + } + } } LintLevelSource::CommandLine(lint_flag_val, orig_level) => { let flag = orig_level.to_cmd_flag(); @@ -427,7 +452,7 @@ fn lint_level_impl( decorate(&mut err); } - explain_lint_level_source(lint, level, src, &mut err); + explain_lint_level_source(sess, lint, level, src, &mut err); err.emit() } lint_level_impl(sess, lint, level, span, Box::new(decorate)) diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 7d2fc0995aa0..78cafe8566b6 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -35,14 +35,10 @@ pub struct CodegenFnAttrs { pub inline: InlineAttr, /// Parsed representation of the `#[optimize]` attribute pub optimize: OptimizeAttr, - /// The `#[export_name = "..."]` attribute, indicating a custom symbol a - /// function should be exported under - pub export_name: Option, - /// The `#[link_name = "..."]` attribute, indicating a custom symbol an - /// imported function should be imported as. Note that `export_name` - /// probably isn't set when this is set, this is for foreign items while - /// `#[export_name]` is for Rust-defined functions. - pub link_name: Option, + /// The name this function will be imported/exported under. This can be set + /// using the `#[export_name = "..."]` or `#[link_name = "..."]` attribute + /// depending on if this is a function definition or foreign function. + pub symbol_name: Option, /// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an /// imported function has in the dynamic library. Note that this must not /// be set when `link_name` is set. This is for foreign items with the @@ -61,8 +57,8 @@ pub struct CodegenFnAttrs { /// The `#[link_section = "..."]` attribute, or what executable section this /// should be placed in. pub link_section: Option, - /// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which - /// instrumentation should be disabled inside the annotated function. + /// The `#[sanitize(xyz = "off")]` attribute. Indicates sanitizers for which + /// instrumentation should be disabled inside the function. pub no_sanitize: SanitizerSet, /// The `#[instruction_set(set)]` attribute. Indicates if the generated code should /// be generated against a specific instruction set. Only usable on architectures which allow @@ -76,13 +72,23 @@ pub struct CodegenFnAttrs { pub patchable_function_entry: Option, } +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)] +pub enum TargetFeatureKind { + /// The feature is implied by another feature, rather than explicitly added by the + /// `#[target_feature]` attribute + Implied, + /// The feature is added by the regular `target_feature` attribute. + Enabled, + /// The feature is added by the unsafe `force_target_feature` attribute. + Forced, +} + #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct TargetFeature { /// The name of the target feature (e.g. "avx") pub name: Symbol, - /// The feature is implied by another feature, rather than explicitly added by the - /// `#[target_feature]` attribute - pub implied: bool, + /// The way this feature was enabled. + pub kind: TargetFeatureKind, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] @@ -167,8 +173,7 @@ pub const fn new() -> CodegenFnAttrs { flags: CodegenFnAttrFlags::empty(), inline: InlineAttr::None, optimize: OptimizeAttr::Default, - export_name: None, - link_name: None, + symbol_name: None, link_ordinal: None, target_features: vec![], safe_target_features: false, @@ -196,7 +201,7 @@ pub fn contains_extern_indicator(&self, tcx: TyCtxt<'_>, did: DefId) -> bool { self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) - || self.export_name.is_some() + || self.symbol_name.is_some() || match self.linkage { // These are private, so make sure we don't try to consider // them external. diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 4a19cf1563cf..18520089e3ea 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use rustc_ast::NodeId; -use rustc_errors::{Applicability, Diag, EmissionGuarantee}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintBuffer}; use rustc_feature::GateIssue; use rustc_hir::attrs::{DeprecatedSince, Deprecation}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -12,7 +12,7 @@ use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic}; use rustc_session::Session; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; -use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer}; +use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint}; use rustc_session::parse::feature_err_issue; use rustc_span::{Span, Symbol, sym}; use tracing::debug; 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 ea8596ea2865..dbbd95408c8a 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -120,6 +120,17 @@ pub fn get_byte(&self, offset: Size, cx: &impl HasDataLayout) -> Option<(Prov, u } } + /// Gets the provenances of all bytes (including from pointers) in a range. + pub fn get_range( + &self, + cx: &impl HasDataLayout, + range: AllocRange, + ) -> impl Iterator { + let ptr_provs = self.range_ptrs_get(range, cx).iter().map(|(_, p)| *p); + let byte_provs = self.range_bytes_get(range).iter().map(|(_, (p, _))| *p); + ptr_provs.chain(byte_provs) + } + /// Attempt to merge per-byte provenance back into ptr chunks, if the right fragments /// sit next to each other. Return `false` is that is not possible due to partial pointers. pub fn merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index c55c7fc6002c..c977e5329c29 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -115,48 +115,6 @@ pub fn index(&self) -> (usize, usize) { MirPhase::Runtime(runtime_phase) => (3, 1 + runtime_phase as usize), } } - - /// Parses a `MirPhase` from a pair of strings. Panics if this isn't possible for any reason. - pub fn parse(dialect: String, phase: Option) -> Self { - match &*dialect.to_ascii_lowercase() { - "built" => { - assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR"); - MirPhase::Built - } - "analysis" => Self::Analysis(AnalysisPhase::parse(phase)), - "runtime" => Self::Runtime(RuntimePhase::parse(phase)), - _ => bug!("Unknown MIR dialect: '{}'", dialect), - } - } -} - -impl AnalysisPhase { - pub fn parse(phase: Option) -> Self { - let Some(phase) = phase else { - return Self::Initial; - }; - - match &*phase.to_ascii_lowercase() { - "initial" => Self::Initial, - "post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup, - _ => bug!("Unknown analysis phase: '{}'", phase), - } - } -} - -impl RuntimePhase { - pub fn parse(phase: Option) -> Self { - let Some(phase) = phase else { - return Self::Initial; - }; - - match &*phase.to_ascii_lowercase() { - "initial" => Self::Initial, - "post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup, - "optimized" => Self::Optimized, - _ => bug!("Unknown runtime phase: '{}'", phase), - } - } } /// Where a specific `mir::Body` comes from. diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 0e6f797b1e4f..533066bdef91 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -548,10 +548,27 @@ fn item_sort_key<'tcx>(tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>) -> ItemSortKey<' let mut items: Vec<_> = self.items().iter().map(|(&i, &data)| (i, data)).collect(); if !tcx.sess.opts.unstable_opts.codegen_source_order { - // It's already deterministic, so we can just use it. - return items; + // In this case, we do not need to keep the items in any specific order, as the input + // is already deterministic. + // + // However, it seems that moving related things (such as different + // monomorphizations of the same function) close to one another is actually beneficial + // for LLVM performance. + // LLVM will codegen the items in the order we pass them to it, and when it handles + // similar things in succession, it seems that it leads to better cache utilization, + // less branch mispredictions and in general to better performance. + // For example, if we have functions `a`, `c::`, `b`, `c::`, `d` and + // `c::`, it seems that it helps LLVM's performance to codegen the three `c` + // instantiations right after one another, as they will likely reference similar types, + // call similar functions, etc. + // + // See https://github.com/rust-lang/rust/pull/145358 for more details. + // + // Sorting by symbol name should not incur any new non-determinism. + items.sort_by_cached_key(|&(i, _)| i.symbol_name(tcx)); + } else { + items.sort_by_cached_key(|&(i, _)| item_sort_key(tcx, i)); } - items.sort_by_cached_key(|&(i, _)| item_sort_key(tcx, i)); items } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index f2d5b13cbc88..128ae8549f7c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -929,7 +929,10 @@ pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { } Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"), Unreachable => write!(fmt, "unreachable"), - Drop { place, .. } => write!(fmt, "drop({place:?})"), + Drop { place, async_fut: None, .. } => write!(fmt, "drop({place:?})"), + Drop { place, async_fut: Some(async_fut), .. } => { + write!(fmt, "async drop({place:?}; poll={async_fut:?})") + } Call { func, args, destination, .. } => { write!(fmt, "{destination:?} = ")?; write!(fmt, "{func:?}(")?; diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 683d7b486171..6e52bc775ef7 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -160,7 +160,11 @@ pub fn multi_projection_ty( /// Convenience wrapper around `projection_ty_core` for `PlaceElem`, /// where we can just use the `Ty` that is already stored inline on /// field projection elems. - pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { + pub fn projection_ty( + self, + tcx: TyCtxt<'tcx>, + elem: ProjectionElem>, + ) -> PlaceTy<'tcx> { self.projection_ty_core(tcx, &elem, |ty| ty, |_, _, _, ty| ty, |ty| ty) } @@ -290,6 +294,36 @@ pub fn can_use_in_debuginfo(&self) -> bool { Self::UnwrapUnsafeBinder(..) => false, } } + + /// Returns the `ProjectionKind` associated to this projection. + pub fn kind(self) -> ProjectionKind { + self.try_map(|_| Some(()), |_| ()).unwrap() + } + + /// Apply functions to types and values in this projection and return the result. + pub fn try_map( + self, + v: impl FnOnce(V) -> Option, + t: impl FnOnce(T) -> T2, + ) -> Option> { + Some(match self { + ProjectionElem::Deref => ProjectionElem::Deref, + ProjectionElem::Downcast(name, read_variant) => { + ProjectionElem::Downcast(name, read_variant) + } + ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, t(ty)), + ProjectionElem::ConstantIndex { offset, min_length, from_end } => { + ProjectionElem::ConstantIndex { offset, min_length, from_end } + } + ProjectionElem::Subslice { from, to, from_end } => { + ProjectionElem::Subslice { from, to, from_end } + } + ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(t(ty)), + ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(t(ty)), + ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(t(ty)), + ProjectionElem::Index(val) => ProjectionElem::Index(v(val)?), + }) + } } /// Alias for projections as they appear in `UserTypeProjection`, where we diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 42a68b29ec75..904d78d69b61 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -531,13 +531,20 @@ fn super_terminator( unwind: _, replace: _, drop: _, - async_fut: _, + async_fut, } => { self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Drop), location ); + if let Some(async_fut) = async_fut { + self.visit_local( + $(&$mutability)? *async_fut, + PlaceContext::MutatingUse(MutatingUseContext::Borrow), + location + ); + } } TerminatorKind::Call { diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index a8b357bf105b..ea62461ebeb6 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -343,6 +343,7 @@ impl EraseType for $ty { rustc_span::Symbol, rustc_span::Ident, rustc_target::spec::PanicStrategy, + rustc_target::spec::SanitizerSet, rustc_type_ir::Variance, u32, usize, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f4d0120a2e7b..7bd8a0525a2c 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -100,7 +100,7 @@ use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_target::spec::PanicStrategy; +use rustc_target::spec::{PanicStrategy, SanitizerSet}; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; use crate::infer::canonical::{self, Canonical}; @@ -696,6 +696,22 @@ return_result_from_ensure_ok } + /// Used in case `mir_borrowck` fails to prove an obligation. We generally assume that + /// all goals we prove in MIR type check hold as we've already checked them in HIR typeck. + /// + /// However, we replace each free region in the MIR body with a unique region inference + /// variable. As we may rely on structural identity when proving goals this may cause a + /// goal to no longer hold. We store obligations for which this may happen during HIR + /// typeck in the `TypeckResults`. We then uniquify and reprove them in case MIR typeck + /// encounters an unexpected error. We expect this to result in an error when used and + /// delay a bug if it does not. + query check_potentially_region_dependent_goals(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { + |tcx| "reproving potentially region dependent HIR typeck goals for `{}", + tcx.def_path_str(key) + } + } + /// MIR after our optimization passes have run. This is MIR that is ready /// for codegen. This is also the only query that can fetch non-local MIR, at present. query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { @@ -1115,6 +1131,11 @@ desc { |tcx| "collecting all inherent impls for `{:?}`", key } } + /// Unsafety-check this `LocalDefId`. + query check_transmutes(key: LocalDefId) { + desc { |tcx| "check transmute calls inside `{}`", tcx.def_path_str(key) } + } + /// Unsafety-check this `LocalDefId`. query check_unsafety(key: LocalDefId) { desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } @@ -2686,6 +2707,16 @@ desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } + + /// Checks for the nearest `#[sanitize(xyz = "off")]` or + /// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the + /// crate root. + /// + /// Returns the set of sanitizers that is explicitly disabled for this def. + query disabled_sanitizers_for(key: LocalDefId) -> SanitizerSet { + desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) } + feedable + } } rustc_with_all_queries! { define_callbacks! } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index a7ac34428986..546791135ba6 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -645,34 +645,29 @@ fn decode_span(&mut self) -> Span { let parent = Option::::decode(self); let tag: u8 = Decodable::decode(self); - if tag == TAG_PARTIAL_SPAN { - return Span::new(BytePos(0), BytePos(0), ctxt, parent); - } else if tag == TAG_RELATIVE_SPAN { - let dlo = u32::decode(self); - let dto = u32::decode(self); + let (lo, hi) = match tag { + TAG_PARTIAL_SPAN => (BytePos(0), BytePos(0)), + TAG_RELATIVE_SPAN => { + let dlo = u32::decode(self); + let dto = u32::decode(self); - let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked(); - let span = Span::new( - enclosing.lo + BytePos::from_u32(dlo), - enclosing.lo + BytePos::from_u32(dto), - ctxt, - parent, - ); + let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked(); + (enclosing.lo + BytePos::from_u32(dlo), enclosing.lo + BytePos::from_u32(dto)) + } + TAG_FULL_SPAN => { + let file_lo_index = SourceFileIndex::decode(self); + let line_lo = usize::decode(self); + let col_lo = RelativeBytePos::decode(self); + let len = BytePos::decode(self); - return span; - } else { - debug_assert_eq!(tag, TAG_FULL_SPAN); - } - - let file_lo_index = SourceFileIndex::decode(self); - let line_lo = usize::decode(self); - let col_lo = RelativeBytePos::decode(self); - let len = BytePos::decode(self); - - let file_lo = self.file_index_to_file(file_lo_index); - let lo = file_lo.lines()[line_lo - 1] + col_lo; - let lo = file_lo.absolute_position(lo); - let hi = lo + len; + let file_lo = self.file_index_to_file(file_lo_index); + let lo = file_lo.lines()[line_lo - 1] + col_lo; + let lo = file_lo.absolute_position(lo); + let hi = lo + len; + (lo, hi) + } + _ => unreachable!(), + }; Span::new(lo, hi, ctxt, parent) } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 5bdde3a514e4..32f91bfba6be 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -760,6 +760,9 @@ pub enum DynCompatibilityViolation { // Supertrait has a non-lifetime `for` binder. SupertraitNonLifetimeBinder(SmallVec<[Span; 1]>), + // Trait has a `const Trait` supertrait. + SupertraitConst(SmallVec<[Span; 1]>), + /// Method has something illegal. Method(Symbol, MethodViolationCode, Span), @@ -785,6 +788,9 @@ pub fn error_msg(&self) -> Cow<'static, str> { DynCompatibilityViolation::SupertraitNonLifetimeBinder(_) => { "where clause cannot reference non-lifetime `for<...>` variables".into() } + DynCompatibilityViolation::SupertraitConst(_) => { + "it cannot have a `const` supertrait".into() + } DynCompatibilityViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { format!("associated function `{name}` has no `self` parameter").into() } @@ -842,7 +848,8 @@ pub fn solution(&self) -> DynCompatibilityViolationSolution { match self { DynCompatibilityViolation::SizedSelf(_) | DynCompatibilityViolation::SupertraitSelf(_) - | DynCompatibilityViolation::SupertraitNonLifetimeBinder(..) => { + | DynCompatibilityViolation::SupertraitNonLifetimeBinder(..) + | DynCompatibilityViolation::SupertraitConst(_) => { DynCompatibilityViolationSolution::None } DynCompatibilityViolation::Method( @@ -873,15 +880,17 @@ pub fn solution(&self) -> DynCompatibilityViolationSolution { match self { DynCompatibilityViolation::SupertraitSelf(spans) | DynCompatibilityViolation::SizedSelf(spans) - | DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans) => spans.clone(), + | DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans) + | DynCompatibilityViolation::SupertraitConst(spans) => spans.clone(), DynCompatibilityViolation::AssocConst(_, span) | DynCompatibilityViolation::GAT(_, span) - | DynCompatibilityViolation::Method(_, _, span) - if *span != DUMMY_SP => - { - smallvec![*span] + | DynCompatibilityViolation::Method(_, _, span) => { + if *span != DUMMY_SP { + smallvec![*span] + } else { + smallvec![] + } } - _ => smallvec![], } } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 49a4733de3b0..4990a85466eb 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -33,7 +33,7 @@ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, LintEmitter, MultiSpan, }; use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::{CtorKind, DefKind}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState}; use rustc_hir::intravisit::VisitorExt; @@ -445,7 +445,10 @@ fn impl_is_const(self, def_id: DefId) -> bool { } fn fn_is_const(self, def_id: DefId) -> bool { - debug_assert_matches!(self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn); + debug_assert_matches!( + self.def_kind(def_id), + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(CtorOf::Struct, CtorKind::Fn) + ); self.is_conditionally_const(def_id) } @@ -1418,6 +1421,8 @@ pub struct TyCtxt<'tcx> { } impl<'tcx> LintEmitter for TyCtxt<'tcx> { + type Id = HirId; + fn emit_node_span_lint( self, lint: &'static Lint, diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index b3042904a296..b9a6f67ab0dc 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -23,7 +23,7 @@ impl IntoDiagArg for Ty<'_> { fn into_diag_arg(self, path: &mut Option) -> rustc_errors::DiagArgValue { ty::tls::with(|tcx| { let ty = tcx.short_string(self, path); - rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(ty)) + DiagArgValue::Str(std::borrow::Cow::Owned(ty)) }) } } @@ -32,7 +32,7 @@ impl IntoDiagArg for Instance<'_> { fn into_diag_arg(self, path: &mut Option) -> rustc_errors::DiagArgValue { ty::tls::with(|tcx| { let instance = tcx.short_string_namespace(self, path, Namespace::ValueNS); - rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(instance)) + DiagArgValue::Str(std::borrow::Cow::Owned(instance)) }) } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e70c98ab7042..a7298af502ef 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -32,7 +32,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer}; use rustc_hir::attrs::{AttributeKind, StrippedCfgItem}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; @@ -46,7 +46,6 @@ }; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; -use rustc_session::lint::LintBuffer; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, sym}; diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index e6feafea1228..9e6f277ef770 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -124,6 +124,15 @@ fn print_path_with_qualified( trait_ref: Option>, ) -> Result<(), PrintError>; + fn print_coroutine_with_kind( + &mut self, + def_id: DefId, + parent_args: &'tcx [GenericArg<'tcx>], + kind: Ty<'tcx>, + ) -> Result<(), PrintError> { + self.print_path_with_generic_args(|p| p.print_def_path(def_id, parent_args), &[kind.into()]) + } + // Defaults (should not be overridden): #[instrument(skip(self), level = "debug")] @@ -162,9 +171,10 @@ fn default_print_def_path( )) = self.tcx().coroutine_kind(def_id) && args.len() > parent_args.len() { - return self.print_path_with_generic_args( - |p| p.print_def_path(def_id, parent_args), - &args[..parent_args.len() + 1][..1], + return self.print_coroutine_with_kind( + def_id, + parent_args, + args[parent_args.len()].expect_ty(), ); } else { // Closures' own generics are only captures, don't print them. diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 72474a605669..755fc68d86f3 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -821,10 +821,38 @@ pub fn new_coroutine( #[inline] pub fn new_coroutine_witness( tcx: TyCtxt<'tcx>, - id: DefId, + def_id: DefId, args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { - Ty::new(tcx, CoroutineWitness(id, args)) + if cfg!(debug_assertions) { + tcx.debug_assert_args_compatible(tcx.typeck_root_def_id(def_id), args); + } + Ty::new(tcx, CoroutineWitness(def_id, args)) + } + + pub fn new_coroutine_witness_for_coroutine( + tcx: TyCtxt<'tcx>, + def_id: DefId, + coroutine_args: GenericArgsRef<'tcx>, + ) -> Ty<'tcx> { + tcx.debug_assert_args_compatible(def_id, coroutine_args); + // HACK: Coroutine witness types are lifetime erased, so they + // never reference any lifetime args from the coroutine. We erase + // the regions here since we may get into situations where a + // coroutine is recursively contained within itself, leading to + // witness types that differ by region args. This means that + // cycle detection in fulfillment will not kick in, which leads + // to unnecessary overflows in async code. See the issue: + // . + let args = + ty::GenericArgs::for_item(tcx, tcx.typeck_root_def_id(def_id), |def, _| { + match def.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => coroutine_args[def.index as usize], + } + }); + Ty::new_coroutine_witness(tcx, def_id, args) } // misc @@ -985,6 +1013,14 @@ fn new_coroutine_witness( Ty::new_coroutine_witness(interner, def_id, args) } + fn new_coroutine_witness_for_coroutine( + interner: TyCtxt<'tcx>, + def_id: DefId, + coroutine_args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_coroutine_witness_for_coroutine(interner, def_id, coroutine_args) + } + fn new_ptr(interner: TyCtxt<'tcx>, ty: Self, mutbl: hir::Mutability) -> Self { Ty::new_ptr(interner, ty, mutbl) } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 6b187c5325a9..8dd80aab946d 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -206,10 +206,25 @@ pub struct TypeckResults<'tcx> { /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, + /// Goals proven during HIR typeck which may be potentially region dependent. + /// + /// Borrowck *uniquifies* regions which may cause these goal to be ambiguous in MIR + /// type check. We ICE if goals fail in borrowck to detect bugs during MIR building or + /// missed checks in HIR typeck. To avoid ICE due to region dependence we store all + /// goals which may be region dependent and reprove them in case borrowck encounters + /// an error. + pub potentially_region_dependent_goals: + FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, + /// Contains the data for evaluating the effect of feature `capture_disjoint_fields` /// on closure size. pub closure_size_eval: LocalDefIdMap>, + /// Stores the types involved in calls to `transmute` intrinsic. These are meant to be checked + /// outside of typeck and borrowck to avoid cycles with opaque types and coroutine layout + /// computation. + pub transmutes_to_check: Vec<(Ty<'tcx>, Ty<'tcx>, HirId)>, + /// Container types and field indices of `offset_of!` expressions offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)>, } @@ -240,7 +255,9 @@ pub fn new(hir_owner: OwnerId) -> TypeckResults<'tcx> { closure_fake_reads: Default::default(), rvalue_scopes: Default::default(), coroutine_stalled_predicates: Default::default(), + potentially_region_dependent_goals: Default::default(), closure_size_eval: Default::default(), + transmutes_to_check: Default::default(), offset_of_data: Default::default(), } } diff --git a/compiler/rustc_mir_build/src/builder/coverageinfo.rs b/compiler/rustc_mir_build/src/builder/coverageinfo.rs index 14199c209217..091b9dad5bc1 100644 --- a/compiler/rustc_mir_build/src/builder/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/builder/coverageinfo.rs @@ -157,6 +157,21 @@ pub(crate) fn into_done(self) -> Box { // if there's nothing interesting in it. Box::new(CoverageInfoHi { num_block_markers, branch_spans }) } + + pub(crate) fn as_done(&self) -> Box { + let &Self { nots: _, markers: BlockMarkerGen { num_block_markers }, ref branch_info } = + self; + + let branch_spans = branch_info + .as_ref() + .map(|branch_info| branch_info.branch_spans.as_slice()) + .unwrap_or_default() + .to_owned(); + + // For simplicity, always return an info struct (without Option), even + // if there's nothing interesting in it. + Box::new(CoverageInfoHi { num_block_markers, branch_spans }) + } } impl<'tcx> Builder<'_, 'tcx> { diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index 902a6e7f115b..792ad6d782cf 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -19,10 +19,10 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; -use rustc_hir::{Attribute, HirId}; +use rustc_hir::{HirId, attrs}; use rustc_index::{IndexSlice, IndexVec}; +use rustc_middle::bug; use rustc_middle::mir::*; -use rustc_middle::span_bug; use rustc_middle::thir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; @@ -39,7 +39,8 @@ pub(super) fn build_custom_mir<'tcx>( return_ty: Ty<'tcx>, return_ty_span: Span, span: Span, - attr: &Attribute, + dialect: Option, + phase: Option, ) -> Body<'tcx> { let mut body = Body { basic_blocks: BasicBlocks::new(IndexVec::new()), @@ -72,7 +73,7 @@ pub(super) fn build_custom_mir<'tcx>( inlined_parent_scope: None, local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }), }); - body.injection_phase = Some(parse_attribute(attr)); + body.injection_phase = Some(parse_attribute(dialect, phase)); let mut pctxt = ParseCtxt { tcx, @@ -98,40 +99,38 @@ pub(super) fn build_custom_mir<'tcx>( body } -fn parse_attribute(attr: &Attribute) -> MirPhase { - let meta_items = attr.meta_item_list().unwrap(); - let mut dialect: Option = None; - let mut phase: Option = None; - - // Not handling errors properly for this internal attribute; will just abort on errors. - for nested in meta_items { - let name = nested.name().unwrap(); - let value = nested.value_str().unwrap().as_str().to_string(); - match name.as_str() { - "dialect" => { - assert!(dialect.is_none()); - dialect = Some(value); - } - "phase" => { - assert!(phase.is_none()); - phase = Some(value); - } - other => { - span_bug!( - nested.span(), - "Unexpected key while parsing custom_mir attribute: '{}'", - other - ); - } - } - } - +/// Turns the arguments passed to `#[custom_mir(..)]` into a proper +/// [`MirPhase`]. Panics if this isn't possible for any reason. +fn parse_attribute(dialect: Option, phase: Option) -> MirPhase { let Some(dialect) = dialect else { + // Caught during attribute checking. assert!(phase.is_none()); return MirPhase::Built; }; - MirPhase::parse(dialect, phase) + match dialect { + attrs::MirDialect::Built => { + // Caught during attribute checking. + assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR"); + MirPhase::Built + } + attrs::MirDialect::Analysis => match phase { + None | Some(attrs::MirPhase::Initial) => MirPhase::Analysis(AnalysisPhase::Initial), + + Some(attrs::MirPhase::PostCleanup) => MirPhase::Analysis(AnalysisPhase::PostCleanup), + + Some(attrs::MirPhase::Optimized) => { + // Caught during attribute checking. + bug!("`optimized` dialect is not compatible with the `analysis` dialect") + } + }, + + attrs::MirDialect::Runtime => match phase { + None | Some(attrs::MirPhase::Initial) => MirPhase::Runtime(RuntimePhase::Initial), + Some(attrs::MirPhase::PostCleanup) => MirPhase::Runtime(RuntimePhase::PostCleanup), + Some(attrs::MirPhase::Optimized) => MirPhase::Runtime(RuntimePhase::Optimized), + }, + } } struct ParseCtxt<'a, 'tcx> { diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 855cd2f3bc0a..6a8f0b21ee0e 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -11,9 +11,10 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_errors::ErrorGuaranteed; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node, find_attr}; use rustc_index::bit_set::GrowableBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -479,8 +480,7 @@ fn construct_fn<'tcx>( ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"), }; - if let Some(custom_mir_attr) = - tcx.hir_attrs(fn_id).iter().find(|attr| attr.has_name(sym::custom_mir)) + if let Some((dialect, phase)) = find_attr!(tcx.hir_attrs(fn_id), AttributeKind::CustomMir(dialect, phase, _) => (dialect, phase)) { return custom::build_custom_mir( tcx, @@ -492,7 +492,8 @@ fn construct_fn<'tcx>( return_ty, return_ty_span, span_with_body, - custom_mir_attr, + dialect.as_ref().map(|(d, _)| *d), + phase.as_ref().map(|(p, _)| *p), ); } @@ -789,6 +790,28 @@ fn new( builder } + #[allow(dead_code)] + fn dump_for_debugging(&self) { + let mut body = Body::new( + MirSource::item(self.def_id.to_def_id()), + self.cfg.basic_blocks.clone(), + self.source_scopes.clone(), + self.local_decls.clone(), + self.canonical_user_type_annotations.clone(), + self.arg_count.clone(), + self.var_debug_info.clone(), + self.fn_span.clone(), + self.coroutine.clone(), + None, + ); + body.coverage_info_hi = self.coverage_info.as_ref().map(|b| b.as_done()); + + use rustc_middle::mir::pretty; + let options = pretty::PrettyPrintMirOptions::from_cli(self.tcx); + pretty::write_mir_fn(self.tcx, &body, &mut |_, _| Ok(()), &mut std::io::stdout(), options) + .unwrap(); + } + fn finish(self) -> Body<'tcx> { let mut body = Body::new( MirSource::item(self.def_id.to_def_id()), diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index d4b6da2c14b7..e0cbe8519edd 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -135,30 +135,23 @@ fn check_tail_call(&mut self, call: &Expr<'_>, expr: &Expr<'_>) { self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi); } + // FIXME(explicit_tail_calls): this currently fails for cases where opaques are used. + // e.g. + // ``` + // fn a() -> impl Sized { become b() } // ICE + // fn b() -> u8 { 0 } + // ``` + // we should think what is the expected behavior here. + // (we should probably just accept this by revealing opaques?) if caller_sig.inputs_and_output != callee_sig.inputs_and_output { - if caller_sig.inputs() != callee_sig.inputs() { - self.report_arguments_mismatch( - expr.span, - self.tcx.liberate_late_bound_regions( - CRATE_DEF_ID.to_def_id(), - self.caller_ty.fn_sig(self.tcx), - ), - self.tcx - .liberate_late_bound_regions(CRATE_DEF_ID.to_def_id(), ty.fn_sig(self.tcx)), - ); - } - - // FIXME(explicit_tail_calls): this currently fails for cases where opaques are used. - // e.g. - // ``` - // fn a() -> impl Sized { become b() } // ICE - // fn b() -> u8 { 0 } - // ``` - // we should think what is the expected behavior here. - // (we should probably just accept this by revealing opaques?) - if caller_sig.output() != callee_sig.output() { - span_bug!(expr.span, "hir typeck should have checked the return type already"); - } + self.report_signature_mismatch( + expr.span, + self.tcx.liberate_late_bound_regions( + CRATE_DEF_ID.to_def_id(), + self.caller_ty.fn_sig(self.tcx), + ), + self.tcx.liberate_late_bound_regions(CRATE_DEF_ID.to_def_id(), ty.fn_sig(self.tcx)), + ); } { @@ -365,7 +358,7 @@ fn report_abi_mismatch(&mut self, sp: Span, caller_abi: ExternAbi, callee_abi: E self.found_errors = Err(err); } - fn report_arguments_mismatch( + fn report_signature_mismatch( &mut self, sp: Span, caller_sig: ty::FnSig<'_>, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 0b6b36640e92..b5e165c75170 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -5,9 +5,10 @@ use rustc_ast::AsmMacro; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::DiagArgValue; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; -use rustc_middle::middle::codegen_fn_attrs::TargetFeature; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr}; +use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; use rustc_middle::thir::visit::Visitor; @@ -521,7 +522,7 @@ fn visit_expr(&mut self, expr: &'a Expr<'tcx>) { .iter() .copied() .filter(|feature| { - !feature.implied + feature.kind == TargetFeatureKind::Enabled && !self .body_target_features .iter() @@ -1157,7 +1158,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { // Closures and inline consts are handled by their owner, if it has a body assert!(!tcx.is_typeck_child(def.to_def_id())); // Also, don't safety check custom MIR - if tcx.has_attr(def, sym::custom_mir) { + if find_attr!(tcx.get_all_attrs(def), AttributeKind::CustomMir(..) => ()).is_some() { return; } diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 24d4136c6423..9657c4dc839d 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -4,11 +4,11 @@ use rustc_data_structures::steal::Steal; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; -use rustc_hir::HirId; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; +use rustc_hir::{self as hir, HirId, find_attr}; use rustc_middle::bug; use rustc_middle::middle::region; use rustc_middle::thir::*; @@ -111,10 +111,8 @@ fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self { typeck_results, rvalue_scopes: &typeck_results.rvalue_scopes, body_owner: def.to_def_id(), - apply_adjustments: tcx - .hir_attrs(hir_id) - .iter() - .all(|attr| !attr.has_name(rustc_span::sym::custom_mir)), + apply_adjustments: + !find_attr!(tcx.hir_attrs(hir_id), AttributeKind::CustomMir(..) => ()).is_some(), } } diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index 293bcbef21b8..9621f9f20bdc 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -13,7 +13,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_graphviz = { path = "../rustc_graphviz" } -rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index f86a15a8f921..372a3f3a8b81 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -8,7 +8,6 @@ use std::{io, ops, str}; use regex::Regex; -use rustc_hir::def_id::DefId; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name, @@ -16,6 +15,7 @@ }; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_span::def_id::DefId; use rustc_span::{Symbol, sym}; use tracing::debug; use {rustc_ast as ast, rustc_graphviz as dot}; diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs deleted file mode 100644 index d056ad3d4b4c..000000000000 --- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! The move-analysis portion of borrowck needs to work in an abstract -//! domain of lifted `Place`s. Most of the `Place` variants fall into a -//! one-to-one mapping between the concrete and abstract (e.g., a -//! field-deref on a local variable, `x.field`, has the same meaning -//! in both domains). Indexed projections are the exception: `a[x]` -//! needs to be treated as mapping to the same move path as `a[y]` as -//! well as `a[13]`, etc. So we map these `x`/`y` values to `()`. -//! -//! (In theory, the analysis could be extended to work with sets of -//! paths, so that `a[0]` and `a[13]` could be kept distinct, while -//! `a[x]` would still overlap them both. But that is not this -//! representation does today.) - -use rustc_middle::mir::{PlaceElem, ProjectionElem, ProjectionKind}; - -pub(crate) trait Lift { - fn lift(&self) -> ProjectionKind; -} - -impl<'tcx> Lift for PlaceElem<'tcx> { - fn lift(&self) -> ProjectionKind { - match *self { - ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(f, _ty) => ProjectionElem::Field(f, ()), - ProjectionElem::OpaqueCast(_ty) => ProjectionElem::OpaqueCast(()), - ProjectionElem::Index(_i) => ProjectionElem::Index(()), - ProjectionElem::Subslice { from, to, from_end } => { - ProjectionElem::Subslice { from, to, from_end } - } - ProjectionElem::ConstantIndex { offset, min_length, from_end } => { - ProjectionElem::ConstantIndex { offset, min_length, from_end } - } - ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u), - ProjectionElem::Subtype(_ty) => ProjectionElem::Subtype(()), - ProjectionElem::UnwrapUnsafeBinder(_ty) => ProjectionElem::UnwrapUnsafeBinder(()), - } - } -} diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 8bbc89fdcecb..48718cad597a 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -7,7 +7,6 @@ use smallvec::{SmallVec, smallvec}; use tracing::debug; -use super::abs_domain::Lift; use super::{ Init, InitIndex, InitKind, InitLocation, LocationMap, LookupResult, MoveData, MoveOut, MoveOutIndex, MovePath, MovePathIndex, MovePathLookup, @@ -241,7 +240,7 @@ fn move_path_for(&mut self, place: Place<'tcx>) -> MovePathResult { if union_path.is_none() { // inlined from add_move_path because of a borrowck conflict with the iterator base = - *data.rev_lookup.projections.entry((base, elem.lift())).or_insert_with(|| { + *data.rev_lookup.projections.entry((base, elem.kind())).or_insert_with(|| { new_move_path( &mut data.move_paths, &mut data.path_map, @@ -272,7 +271,7 @@ fn add_move_path( tcx, .. } = self; - *rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || { + *rev_lookup.projections.entry((base, elem.kind())).or_insert_with(move || { new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx)) }) } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 18985ba0da25..466416d63f53 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -1,3 +1,15 @@ +//! The move-analysis portion of borrowck needs to work in an abstract domain of lifted `Place`s. +//! Most of the `Place` variants fall into a one-to-one mapping between the concrete and abstract +//! (e.g., a field projection on a local variable, `x.field`, has the same meaning in both +//! domains). In other words, all field projections for the same field on the same local do not +//! have meaningfully different types if ever. Indexed projections are the exception: `a[x]` needs +//! to be treated as mapping to the same move path as `a[y]` as well as `a[13]`, etc. So we map +//! these `x`/`y` values to `()`. +//! +//! (In theory, the analysis could be extended to work with sets of paths, so that `a[0]` and +//! `a[13]` could be kept distinct, while `a[x]` would still overlap them both. But that is not +//! what this representation does today.) + use std::fmt; use std::ops::{Index, IndexMut}; @@ -8,11 +20,8 @@ use rustc_span::Span; use smallvec::SmallVec; -use self::abs_domain::Lift; use crate::un_derefer::UnDerefer; -mod abs_domain; - rustc_index::newtype_index! { #[orderable] #[debug_format = "mp{}"] @@ -324,7 +333,7 @@ pub fn find(&self, place: PlaceRef<'tcx>) -> LookupResult { }; for (_, elem) in self.un_derefer.iter_projections(place) { - if let Some(&subpath) = self.projections.get(&(result, elem.lift())) { + if let Some(&subpath) = self.projections.get(&(result, elem.kind())) { result = subpath; } else { return LookupResult::Parent(Some(result)); diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 1682f3328574..a899ec1fa884 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -1,7 +1,7 @@ use rustc_ast::MetaItem; -use rustc_hir::def_id::DefId; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::def_id::DefId; use rustc_span::{Span, Symbol, sym}; use tracing::{debug, info}; diff --git a/compiler/rustc_mir_transform/src/check_enums.rs b/compiler/rustc_mir_transform/src/check_enums.rs index 33a87cb98730..12447dc7cbb0 100644 --- a/compiler/rustc_mir_transform/src/check_enums.rs +++ b/compiler/rustc_mir_transform/src/check_enums.rs @@ -48,6 +48,21 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let new_block = split_block(basic_blocks, location); match check { + EnumCheckType::Direct { op_size, .. } + | EnumCheckType::WithNiche { op_size, .. } + if op_size.bytes() == 0 => + { + // It is never valid to use a ZST as a discriminant for an inhabited enum, but that will + // have been caught by the type checker. Do nothing but ensure that a bug has been signaled. + tcx.dcx().span_delayed_bug( + source_info.span, + "cannot build enum discriminant from zero-sized type", + ); + basic_blocks[block].terminator = Some(Terminator { + source_info, + kind: TerminatorKind::Goto { target: new_block }, + }); + } EnumCheckType::Direct { source_op, discr, op_size, valid_discrs } => { insert_direct_enum_check( tcx, diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 5568d42ab8f3..879a20e771d6 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -16,7 +16,6 @@ mod balanced_flow; pub(crate) mod node_flow; -mod union_find; /// Struct containing the results of [`prepare_bcb_counters_data`]. pub(crate) struct BcbCountersData { diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs index 91ed54b8b59f..e063f75887b3 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs @@ -7,13 +7,12 @@ //! (Knuth & Stevenson, 1973). use rustc_data_structures::graph; +use rustc_data_structures::union_find::UnionFind; use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; pub(crate) use rustc_middle::mir::coverage::NodeFlowData; use rustc_middle::mir::coverage::Op; -use crate::coverage::counters::union_find::UnionFind; - #[cfg(test)] mod tests; diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 952da2cdf725..5a13394543b4 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -447,26 +447,9 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option> { Projection(base, elem) => { let base = self.evaluated[base].as_ref()?; - let elem = match elem { - ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Downcast(name, read_variant) => { - ProjectionElem::Downcast(name, read_variant) - } - ProjectionElem::Field(f, ()) => ProjectionElem::Field(f, ty.ty), - ProjectionElem::ConstantIndex { offset, min_length, from_end } => { - ProjectionElem::ConstantIndex { offset, min_length, from_end } - } - ProjectionElem::Subslice { from, to, from_end } => { - ProjectionElem::Subslice { from, to, from_end } - } - ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty.ty), - ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty.ty), - ProjectionElem::UnwrapUnsafeBinder(()) => { - ProjectionElem::UnwrapUnsafeBinder(ty.ty) - } - // This should have been replaced by a `ConstantIndex` earlier. - ProjectionElem::Index(_) => return None, - }; + // `Index` by constants should have been replaced by `ConstantIndex` by + // `simplify_place_projection`. + let elem = elem.try_map(|_| None, |()| ty.ty)?; self.ecx.project(base, elem).discard_err()? } Address { place, kind: _, provenance: _ } => { @@ -476,13 +459,11 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option> { let local = self.locals[place.local]?; let pointer = self.evaluated[local].as_ref()?; let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?; - for proj in place.projection.iter().skip(1) { - // We have no call stack to associate a local with a value, so we cannot - // interpret indexing. - if matches!(proj, ProjectionElem::Index(_)) { - return None; - } - mplace = self.ecx.project(&mplace, proj).discard_err()?; + for elem in place.projection.iter().skip(1) { + // `Index` by constants should have been replaced by `ConstantIndex` by + // `simplify_place_projection`. + let elem = elem.try_map(|_| None, |ty| ty)?; + mplace = self.ecx.project(&mplace, elem).discard_err()?; } let pointer = mplace.to_ref(&self.ecx); ImmTy::from_immediate(pointer, ty).into() @@ -902,27 +883,14 @@ fn try_as_place_elem( proj: ProjectionElem, loc: Location, ) -> Option> { - Some(match proj { - ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(idx, ()) => ProjectionElem::Field(idx, ty), - ProjectionElem::Index(idx) => { - let Some(local) = self.try_as_local(idx, loc) else { - return None; - }; + proj.try_map( + |value| { + let local = self.try_as_local(value, loc)?; self.reused_locals.insert(local); - ProjectionElem::Index(local) - } - ProjectionElem::ConstantIndex { offset, min_length, from_end } => { - ProjectionElem::ConstantIndex { offset, min_length, from_end } - } - ProjectionElem::Subslice { from, to, from_end } => { - ProjectionElem::Subslice { from, to, from_end } - } - ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx), - ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty), - ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty), - ProjectionElem::UnwrapUnsafeBinder(()) => ProjectionElem::UnwrapUnsafeBinder(ty), - }) + Some(local) + }, + |()| ty, + ) } fn simplify_aggregate_to_copy( diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index d1c2d6b508f2..6f61215cee2d 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -79,6 +79,7 @@ fn is_enabled(&self, sess: &rustc_session::Session) -> bool { #[instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!(def_id = ?body.source.def_id()); + move_to_copy_pointers(tcx, body); while propagate_ssa(tcx, body) {} } @@ -87,11 +88,43 @@ fn is_required(&self) -> bool { } } +/// The SSA analysis done by [`SsaLocals`] treats [`Operand::Move`] as a read, even though in +/// general [`Operand::Move`] represents pass-by-pointer where the callee can overwrite the +/// pointee (Miri always considers the place deinitialized). CopyProp has a similar trick to +/// turn [`Operand::Move`] into [`Operand::Copy`] when required for an optimization, but in this +/// pass we just turn all moves of pointers into copies because pointers should be by-value anyway. +fn move_to_copy_pointers<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let mut visitor = MoveToCopyVisitor { tcx, local_decls: &body.local_decls }; + for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() { + visitor.visit_basic_block_data(bb, data); + } + + struct MoveToCopyVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + local_decls: &'a IndexVec>, + } + + impl<'a, 'tcx> MutVisitor<'tcx> for MoveToCopyVisitor<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) { + if let Operand::Move(place) = *operand { + if place.ty(self.local_decls, self.tcx).ty.is_any_ptr() { + *operand = Operand::Copy(place); + } + } + self.super_operand(operand, loc); + } + } +} + fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { let typing_env = body.typing_env(tcx); let ssa = SsaLocals::new(tcx, body, typing_env); - let mut replacer = compute_replacement(tcx, body, &ssa); + let mut replacer = compute_replacement(tcx, body, ssa); debug!(?replacer.targets); debug!(?replacer.allowed_replacements); debug!(?replacer.storage_to_remove); @@ -119,7 +152,7 @@ enum Value<'tcx> { fn compute_replacement<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - ssa: &SsaLocals, + ssa: SsaLocals, ) -> Replacer<'tcx> { let always_live_locals = always_storage_live_locals(body); @@ -138,7 +171,7 @@ fn compute_replacement<'tcx>( // reborrowed references. let mut storage_to_remove = DenseBitSet::new_empty(body.local_decls.len()); - let fully_replaceable_locals = fully_replaceable_locals(ssa); + let fully_replaceable_locals = fully_replaceable_locals(&ssa); // Returns true iff we can use `place` as a pointee. // diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 1bc35e599c70..da05c49756ff 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -25,12 +25,8 @@ enum CanonicalizeInputKind { /// trait candidates relies on it when deciding whether a where-bound /// is trivial. ParamEnv, - /// When canonicalizing predicates, we don't keep `'static`. If we're - /// currently outside of the trait solver and canonicalize the root goal - /// during HIR typeck, we replace each occurance of a region with a - /// unique region variable. See the comment on `InferCtxt::in_hir_typeck` - /// for more details. - Predicate { is_hir_typeck_root_goal: bool }, + /// When canonicalizing predicates, we don't keep `'static`. + Predicate, } /// Whether we're canonicalizing a query input or the query response. @@ -191,7 +187,6 @@ fn canonicalize_param_env( pub fn canonicalize_input>( delegate: &'a D, variables: &'a mut Vec, - is_hir_typeck_root_goal: bool, input: QueryInput, ) -> ty::Canonical> { // First canonicalize the `param_env` while keeping `'static` @@ -201,9 +196,7 @@ pub fn canonicalize_input>( // while *mostly* reusing the canonicalizer from above. let mut rest_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { - is_hir_typeck_root_goal, - }), + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate), variables, variable_lookup_table, @@ -481,31 +474,13 @@ fn fold_region(&mut self, r: I::Region) -> I::Region { } }; - let var = if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { - is_hir_typeck_root_goal: true, - }) = self.canonicalize_mode - { - let var = ty::BoundVar::from(self.variables.len()); - self.variables.push(r.into()); - self.var_kinds.push(kind); - var - } else { - self.get_or_insert_bound_var(r, kind) - }; + let var = self.get_or_insert_bound_var(r, kind); Region::new_anon_bound(self.cx(), self.binder_index, var) } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { - is_hir_typeck_root_goal: true, - }) = self.canonicalize_mode - { - // If we're canonicalizing a root goal during HIR typeck, we - // must not use the `cache` as we want to map each occurrence - // of a region to a unique existential variable. - self.inner_fold_ty(t) - } else if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { + if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { ty } else { let res = self.inner_fold_ty(t); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index faa86734d08c..d25e74e73358 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -75,17 +75,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait( Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()])) } - ty::Coroutine(def_id, args) => { - let coroutine_args = args.as_coroutine(); - Ok(ty::Binder::dummy(vec![ - coroutine_args.tupled_upvars_ty(), - Ty::new_coroutine_witness( - ecx.cx(), - def_id, - ecx.cx().mk_args(coroutine_args.parent_args().as_slice()), - ), - ])) - } + ty::Coroutine(def_id, args) => Ok(ty::Binder::dummy(vec![ + args.as_coroutine().tupled_upvars_ty(), + Ty::new_coroutine_witness_for_coroutine(ecx.cx(), def_id, args), + ])), ty::CoroutineWitness(def_id, args) => Ok(ecx .cx() @@ -251,14 +244,9 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait( Movability::Static => Err(NoSolution), Movability::Movable => { if ecx.cx().features().coroutine_clone() { - let coroutine = args.as_coroutine(); Ok(ty::Binder::dummy(vec![ - coroutine.tupled_upvars_ty(), - Ty::new_coroutine_witness( - ecx.cx(), - def_id, - ecx.cx().mk_args(coroutine.parent_args().as_slice()), - ), + args.as_coroutine().tupled_upvars_ty(), + Ty::new_coroutine_witness_for_coroutine(ecx.cx(), def_id, args), ])) } else { Err(NoSolution) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 9a22bf58c035..b7ae9994c627 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -6,7 +6,7 @@ use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; -use rustc_type_ir::{self as ty, Interner, elaborate}; +use rustc_type_ir::{self as ty, Interner, TypingMode, elaborate}; use tracing::instrument; use super::assembly::{Candidate, structural_traits}; @@ -135,12 +135,16 @@ fn consider_impl_candidate( } let impl_polarity = cx.impl_polarity(impl_def_id); - match impl_polarity { + let certainty = match impl_polarity { ty::ImplPolarity::Negative => return Err(NoSolution), - ty::ImplPolarity::Reservation => { - unimplemented!("reservation impl for const trait: {:?}", goal) - } - ty::ImplPolarity::Positive => {} + ty::ImplPolarity::Reservation => match ecx.typing_mode() { + TypingMode::Coherence => Certainty::AMBIGUOUS, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => return Err(NoSolution), + }, + ty::ImplPolarity::Positive => Certainty::Yes, }; if !cx.impl_is_const(impl_def_id) { @@ -171,7 +175,7 @@ fn consider_impl_candidate( }); ecx.add_goals(GoalSource::ImplWhereBound, const_conditions); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.evaluate_added_goals_and_make_canonical_response(certainty) }) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 74c5b49ea92f..4644b145b18a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -57,7 +57,6 @@ impl EvalCtxt<'_, D> /// This expects `goal` and `opaque_types` to be eager resolved. pub(super) fn canonicalize_goal( &self, - is_hir_typeck_root_goal: bool, goal: Goal, opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, ) -> (Vec, CanonicalInput) { @@ -65,7 +64,6 @@ pub(super) fn canonicalize_goal( let canonical = Canonicalizer::canonicalize_input( self.delegate, &mut orig_values, - is_hir_typeck_root_goal, QueryInput { goal, predefined_opaques_in_body: self diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index a4738306181c..0230f784e469 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -459,10 +459,7 @@ pub(super) fn evaluate_goal_raw( let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let is_hir_typeck_root_goal = matches!(goal_evaluation_kind, GoalEvaluationKind::Root) - && self.delegate.in_hir_typeck(); - let (orig_values, canonical_goal) = - self.canonicalize_goal(is_hir_typeck_root_goal, goal, opaque_types); + let (orig_values, canonical_goal) = self.canonicalize_goal(goal, opaque_types); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); let canonical_result = self.search_graph.evaluate_goal( diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 04ede365a214..891ecab041a3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -6,8 +6,8 @@ use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::solve::{CanonicalResponse, SizedTraitKind}; use rustc_type_ir::{ - self as ty, Interner, Movability, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, - Upcast as _, elaborate, + self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, + TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, }; use tracing::{debug, instrument, trace}; @@ -133,19 +133,26 @@ fn trait_def_id_matches( cx: I, clause_def_id: I::DefId, goal_def_id: I::DefId, + polarity: PredicatePolarity, ) -> bool { clause_def_id == goal_def_id // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so // check for a `MetaSized` supertrait being matched against a `Sized` assumption. // // `PointeeSized` bounds are syntactic sugar for a lack of bounds so don't need this. - || (cx.is_lang_item(clause_def_id, TraitSolverLangItem::Sized) + || (polarity == PredicatePolarity::Positive + && cx.is_lang_item(clause_def_id, TraitSolverLangItem::Sized) && cx.is_lang_item(goal_def_id, TraitSolverLangItem::MetaSized)) } if let Some(trait_clause) = assumption.as_trait_clause() && trait_clause.polarity() == goal.predicate.polarity - && trait_def_id_matches(ecx.cx(), trait_clause.def_id(), goal.predicate.def_id()) + && trait_def_id_matches( + ecx.cx(), + trait_clause.def_id(), + goal.predicate.def_id(), + goal.predicate.polarity, + ) && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( goal.predicate.trait_ref.args, trait_clause.skip_binder().trait_ref.args, @@ -168,6 +175,8 @@ fn match_assumption( // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so // check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds // are syntactic sugar for a lack of bounds so don't need this. + // We don't need to check polarity, `fast_reject_assumption` already rejected non-`Positive` + // polarity `Sized` assumptions as matching non-`Positive` `MetaSized` goals. if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized) && ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized) { diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index 0ae0b613fa27..6d738a103715 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -9,7 +9,6 @@ bitflags = "2.4.1" rustc-literal-escaper = "0.0.5" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9e0075c21b9e..4ca2f57bd875 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -359,6 +359,20 @@ parse_generics_in_path = unexpected generic arguments in path parse_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml` parse_help_set_edition_standalone = pass `--edition {$edition}` to `rustc` + +parse_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} + .label = this {$label} contains {$count -> + [one] an invisible + *[other] invisible + } unicode text flow control {$count -> + [one] codepoint + *[other] 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_remove = if their presence wasn't intentional, you can remove them + .suggestion_escape = if you want to keep them but make them visible in your source code, you can escape them + .no_suggestion_note_escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped} + parse_if_expression_missing_condition = missing condition for `if` expression .condition_label = expected condition here .block_label = if this block is the condition of the `if` expression, then it must be followed by another block @@ -436,11 +450,6 @@ parse_inner_doc_comment_not_permitted = expected outer doc comment .label_does_not_annotate_this = the inner doc comment doesn't annotate this {$item} .sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style -parse_invalid_attr_unsafe = `{$name}` is not an unsafe attribute - .label = this is not an unsafe attribute - .suggestion = remove the `unsafe(...)` - .note = extraneous unsafe is not allowed in attributes - parse_invalid_block_macro_segment = cannot use a `block` macro fragment here .label = the `block` fragment is within this context .suggestion = wrap this in another block @@ -468,9 +477,6 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else` parse_invalid_identifier_with_leading_number = identifiers cannot start with a number -parse_invalid_label = - invalid label name `{$name}` - parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid .label = invalid suffix `{$suffix}` .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases @@ -500,6 +506,8 @@ parse_invalid_unicode_escape = invalid unicode character escape parse_invalid_variable_declaration = invalid variable declaration +parse_keyword_label = labels cannot use keyword names + parse_keyword_lifetime = lifetimes cannot use keyword names @@ -601,7 +609,6 @@ parse_maybe_report_ambiguous_plus = ambiguous `+` in a type .suggestion = use parentheses to disambiguate -parse_meta_bad_delim = wrong meta list delimiters parse_meta_bad_delim_suggestion = the delimiters should be `(` and `)` parse_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter}` @@ -748,9 +755,6 @@ parse_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call .suggestion_braces_for_struct = if `{$type}` is a struct, use braces as delimiters .suggestion_no_fields_for_fn = if `{$type}` is a function, use the arguments directly -parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported -parse_parenthesized_lifetime_suggestion = remove the parentheses - parse_path_double_colon = path separator must be a double colon .suggestion = use a double colon instead @@ -993,10 +997,6 @@ parse_unmatched_angle_brackets = {$num_extra_brackets -> *[other] remove extra angle brackets } -parse_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe - .label = usage of unsafe attribute -parse_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` - parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped .label = {parse_unskipped_whitespace} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index a105dd1909e9..797d4830c2ff 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -8,8 +8,9 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic, + SuggestionStyle, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::{Ident, Span, Symbol}; @@ -2228,11 +2229,10 @@ pub(crate) struct KeywordLifetime { } #[derive(Diagnostic)] -#[diag(parse_invalid_label)] -pub(crate) struct InvalidLabel { +#[diag(parse_keyword_label)] +pub(crate) struct KeywordLabel { #[primary_span] pub span: Span, - pub name: Symbol, } #[derive(Diagnostic)] @@ -3163,27 +3163,6 @@ pub(crate) struct ModifierLifetime { pub modifier: &'static str, } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - parse_parenthesized_lifetime_suggestion, - applicability = "machine-applicable" -)] -pub(crate) struct RemoveParens { - #[suggestion_part(code = "")] - pub lo: Span, - #[suggestion_part(code = "")] - pub hi: Span, -} - -#[derive(Diagnostic)] -#[diag(parse_parenthesized_lifetime)] -pub(crate) struct ParenthesizedLifetime { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sugg: RemoveParens, -} - #[derive(Diagnostic)] #[diag(parse_underscore_literal_suffix)] pub(crate) struct UnderscoreLiteralSuffix { @@ -3366,15 +3345,6 @@ pub(crate) struct KwBadCase<'a> { pub kw: &'a str, } -#[derive(Diagnostic)] -#[diag(parse_meta_bad_delim)] -pub(crate) struct MetaBadDelim { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sugg: MetaBadDelimSugg, -} - #[derive(Diagnostic)] #[diag(parse_cfg_attr_bad_delim)] pub(crate) struct CfgAttrBadDelim { @@ -3514,38 +3484,6 @@ pub(crate) struct DotDotRangeAttribute { pub span: Span, } -#[derive(Diagnostic)] -#[diag(parse_invalid_attr_unsafe)] -#[note] -pub(crate) struct InvalidAttrUnsafe { - #[primary_span] - #[label] - pub span: Span, - pub name: Path, -} - -#[derive(Diagnostic)] -#[diag(parse_unsafe_attr_outside_unsafe)] -pub(crate) struct UnsafeAttrOutsideUnsafe { - #[primary_span] - #[label] - pub span: Span, - #[subdiagnostic] - pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - parse_unsafe_attr_outside_unsafe_suggestion, - applicability = "machine-applicable" -)] -pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { - #[suggestion_part(code = "unsafe(")] - pub left: Span, - #[suggestion_part(code = ")")] - pub right: Span, -} - #[derive(Diagnostic)] #[diag(parse_binder_before_modifiers)] pub(crate) struct BinderBeforeModifiers { @@ -3664,3 +3602,76 @@ pub(crate) struct ExpectedRegisterClassOrExplicitRegister { #[primary_span] pub(crate) span: Span, } + +#[derive(LintDiagnostic)] +#[diag(parse_hidden_unicode_codepoints)] +#[note] +pub(crate) struct HiddenUnicodeCodepointsDiag { + pub label: String, + pub count: usize, + #[label] + pub span_label: Span, + #[subdiagnostic] + pub labels: Option, + #[subdiagnostic] + pub sub: HiddenUnicodeCodepointsDiagSub, +} + +pub(crate) struct HiddenUnicodeCodepointsDiagLabels { + pub spans: Vec<(char, Span)>, +} + +impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { + for (c, span) in self.spans { + diag.span_label(span, format!("{c:?}")); + } + } +} + +pub(crate) enum HiddenUnicodeCodepointsDiagSub { + Escape { spans: Vec<(char, Span)> }, + NoEscape { spans: Vec<(char, Span)> }, +} + +// Used because of multiple multipart_suggestion and note +impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { + match self { + HiddenUnicodeCodepointsDiagSub::Escape { spans } => { + diag.multipart_suggestion_with_style( + fluent::parse_suggestion_remove, + spans.iter().map(|(_, span)| (*span, "".to_string())).collect(), + Applicability::MachineApplicable, + SuggestionStyle::HideCodeAlways, + ); + diag.multipart_suggestion( + fluent::parse_suggestion_escape, + spans + .into_iter() + .map(|(c, span)| { + let c = format!("{c:?}"); + (span, c[1..c.len() - 1].to_string()) + }) + .collect(), + Applicability::MachineApplicable, + ); + } + HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => { + // FIXME: in other suggestions we've reversed the inner spans of doc comments. We + // should do the same here to provide the same good suggestions as we do for + // literals above. + diag.arg( + "escaped", + spans + .into_iter() + .map(|(c, _)| format!("{c:?}")) + .collect::>() + .join(", "), + ); + diag.note(fluent::parse_suggestion_remove); + diag.note(fluent::parse_no_suggestion_note_escape); + } + } + } +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 85af5a615aef..9792240a5485 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -49,6 +49,7 @@ pub(crate) fn lex_token_trees<'psess, 'src>( mut src: &'src str, mut start_pos: BytePos, override_span: Option, + frontmatter_allowed: FrontmatterAllowed, ) -> Result>> { // Skip `#!`, if present. if let Some(shebang_len) = rustc_lexer::strip_shebang(src) { @@ -56,7 +57,7 @@ pub(crate) fn lex_token_trees<'psess, 'src>( start_pos = start_pos + BytePos::from_usize(shebang_len); } - let cursor = Cursor::new(src, FrontmatterAllowed::Yes); + let cursor = Cursor::new(src, frontmatter_allowed); let mut lexer = Lexer { psess, start_pos, @@ -542,21 +543,21 @@ fn report_text_direction_codepoint( }) .collect(); + let label = label.to_string(); let count = spans.len(); - let labels = point_at_inner_spans.then_some(spans.clone()); + let labels = point_at_inner_spans + .then_some(errors::HiddenUnicodeCodepointsDiagLabels { spans: spans.clone() }); + let sub = if point_at_inner_spans && !spans.is_empty() { + errors::HiddenUnicodeCodepointsDiagSub::Escape { spans } + } else { + errors::HiddenUnicodeCodepointsDiagSub::NoEscape { spans } + }; self.psess.buffer_lint( TEXT_DIRECTION_CODEPOINT_IN_LITERAL, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::HiddenUnicodeCodepoints { - label: label.to_string(), - count, - span_label: span, - labels, - escape: point_at_inner_spans && !spans.is_empty(), - spans, - }, + errors::HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub }, ); } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 2050c5f96087..48289b2e8abc 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -16,10 +16,11 @@ use std::sync::Arc; use rustc_ast as ast; -use rustc_ast::tokenstream::TokenStream; +use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{AttrItem, Attribute, MetaItemInner, token}; use rustc_ast_pretty::pprust; use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize}; +use rustc_lexer::FrontmatterAllowed; use rustc_session::parse::ParseSess; use rustc_span::source_map::SourceMap; use rustc_span::{FileName, SourceFile, Span}; @@ -30,8 +31,9 @@ #[macro_use] pub mod parser; use parser::Parser; +use rustc_ast::token::Delimiter; + pub mod lexer; -pub mod validate_attr; mod errors; @@ -146,7 +148,7 @@ fn new_parser_from_source_file( source_file: Arc, ) -> Result, Vec>> { let end_pos = source_file.end_position(); - let stream = source_file_to_stream(psess, source_file, None)?; + let stream = source_file_to_stream(psess, source_file, None, FrontmatterAllowed::Yes)?; 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); @@ -161,7 +163,9 @@ pub fn source_str_to_stream( override_span: Option, ) -> Result>> { let source_file = psess.source_map().new_source_file(name, source); - source_file_to_stream(psess, source_file, override_span) + // used mainly for `proc_macro` and the likes, not for our parsing purposes, so don't parse + // frontmatters as frontmatters. + source_file_to_stream(psess, source_file, override_span, FrontmatterAllowed::No) } /// Given a source file, produces a sequence of token trees. Returns any buffered errors from @@ -170,6 +174,7 @@ fn source_file_to_stream<'psess>( psess: &'psess ParseSess, source_file: Arc, override_span: Option, + frontmatter_allowed: FrontmatterAllowed, ) -> Result>> { let src = source_file.src.as_ref().unwrap_or_else(|| { psess.dcx().bug(format!( @@ -178,7 +183,13 @@ fn source_file_to_stream<'psess>( )); }); - lexer::lex_token_trees(psess, src.as_str(), source_file.start_pos, override_span) + lexer::lex_token_trees( + psess, + src.as_str(), + source_file.start_pos, + override_span, + frontmatter_allowed, + ) } /// Runs the given subparser `f` on the tokens of the given `attr`'s item. @@ -225,7 +236,7 @@ pub fn parse_cfg_attr( ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens }) if !tokens.is_empty() => { - crate::validate_attr::check_cfg_attr_bad_delim(psess, dspan, delim); + check_cfg_attr_bad_delim(psess, dspan, delim); match parse_in(psess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { Ok(r) => return Some(r), Err(e) => { @@ -244,3 +255,13 @@ pub fn parse_cfg_attr( } None } + +fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { + if let Delimiter::Parenthesis = delim { + return; + } + psess.dcx().emit_err(errors::CfgAttrBadDelim { + span: span.entire(), + sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, + }); +} diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 7f6afeba28cb..acd338156ce8 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -399,7 +399,7 @@ pub fn parse_cfg_attr( } /// Matches `COMMASEP(meta_item_inner)`. - pub(crate) fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec> { + pub fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec> { // Presumably, the majority of the time there will only be one attr. let mut nmis = ThinVec::with_capacity(1); while self.token != token::Eof { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d0604f763171..1499808966c7 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2077,7 +2077,7 @@ pub(crate) fn mk_token_lit_char(name: Symbol, span: Span) -> (token::Lit, Span) (token::Lit { symbol: name, suffix: None, kind: token::Char }, span) } - fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit { + pub fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit { ast::MetaItemLit { symbol: name, suffix: None, @@ -2086,7 +2086,7 @@ fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit { } } - fn handle_missing_lit( + pub fn handle_missing_lit( &mut self, mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> PResult<'a, L> { @@ -2156,7 +2156,7 @@ fn recover_after_dot(&mut self) { /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and /// `Lit::from_token` (excluding unary negation). - fn eat_token_lit(&mut self) -> Option { + pub fn eat_token_lit(&mut self) -> Option { let check_expr = |expr: Box| { if let ast::ExprKind::Lit(token_lit) = expr.kind { Some(token_lit) @@ -2397,20 +2397,24 @@ fn parse_expr_closure(&mut self) -> PResult<'a, Box> { let before = self.prev_token; let binder = if self.check_keyword(exp!(For)) { let lo = self.token.span; - let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?; + let (bound_vars, _) = self.parse_higher_ranked_binder()?; let span = lo.to(self.prev_token.span); self.psess.gated_spans.gate(sym::closure_lifetime_binder, span); - ClosureBinder::For { span, generic_params: lifetime_defs } + ClosureBinder::For { span, generic_params: bound_vars } } else { ClosureBinder::NotPresent }; let constness = self.parse_closure_constness(); - let movability = - if self.eat_keyword(exp!(Static)) { Movability::Static } else { Movability::Movable }; + let movability = if self.eat_keyword(exp!(Static)) { + self.psess.gated_spans.gate(sym::coroutines, self.prev_token.span); + Movability::Static + } else { + Movability::Movable + }; let coroutine_kind = if self.token_uninterpolated_span().at_least_rust_2018() { self.parse_coroutine_kind(Case::Sensitive) @@ -3090,7 +3094,7 @@ pub(crate) fn eat_label(&mut self) -> Option