Merge ref 'a1dbb44352' from rust-lang/rust

Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: a1dbb44352
Filtered ref: c2339048a82c55166f9b9ee83fd618be252a6e23

This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
The rustc-josh-sync Cronjob Bot 2025-08-25 04:13:22 +00:00
commit 721337b92a
1775 changed files with 34974 additions and 19179 deletions

View file

@ -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

View file

@ -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",
]

View file

@ -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/)
<a id="1.77.0-Compiler"></a>

View file

@ -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",

View file

@ -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"
///

View file

@ -3137,7 +3137,7 @@ impl FnRetTy {
#[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<Box<Item>>, Inline, ModSpans, Result<(), ErrorGuaranteed>),
Loaded(ThinVec<Box<Item>>, Inline, ModSpans),
/// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it.
Unloaded,
}

View file

@ -7,6 +7,7 @@ pub use NtPatKind::*;
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<bool> 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<IdentIsRaw> for bool {
fn from(is_raw: IdentIsRaw) -> bool {
matches!(is_raw, IdentIsRaw::Yes)
impl From<bool> for IdentIsRaw {
fn from(b: bool) -> Self {
if b { Self::Yes } else { Self::No }
}
}

View file

@ -907,6 +907,12 @@ impl TokenTreeCursor {
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

View file

@ -251,7 +251,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
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 @@ impl<'hir> LoweringContext<'_, 'hir> {
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
{

View file

@ -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" }

View file

@ -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

View file

@ -25,16 +25,16 @@ use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
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 @@ impl<'a> AstValidator<'a> {
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<Span> =
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 @@ impl<'a> AstValidator<'a> {
MISSING_ABI,
id,
span,
BuiltinLintDiag::MissingAbi(span, ExternAbi::FALLBACK),
errors::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK },
)
}
}
@ -1169,7 +1180,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
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);

View file

@ -4,7 +4,7 @@ use rustc_abi::ExternAbi;
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<Span>,
pub param_count: usize,
}

View file

@ -524,6 +524,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
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) {

View file

@ -10,7 +10,7 @@ use std::borrow::Cow;
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<Target = pp::Printer> + 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 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
/* 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 */

View file

@ -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"

View file

@ -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.)

View file

@ -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;

View file

@ -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;

View file

@ -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 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
Some(sym::speed) => 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<S: Stage> SingleAttributeParser<S> for CoverageParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
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 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
return None;
};
let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]);
let fail_incorrect_argument =
|span| cx.expected_specific_argument(span, &[sym::on, sym::off]);
let Some(arg) = arg.meta_item() else {
fail_incorrect_argument(args.span);
@ -352,7 +344,7 @@ impl<S: Stage> AttributeParser<S> 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 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
}
}
fn parse_tf_attribute<'c, S: Stage>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = (Symbol, Span)> + '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<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
type Item = (Symbol, Span);
const PATH: &[Symbol] = &[sym::target_feature];
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
const CONVERT: ConvertFn<Self::Item> = |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<Item = Self::Item> + '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 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
Warn(Target::MacroDef),
]);
}
pub(crate) struct ForceTargetFeatureParser;
impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
type Item = (Symbol, Span);
const PATH: &[Symbol] = &[sym::force_target_feature];
const CONVERT: ConvertFn<Self::Item> = |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<Item = Self::Item> + 'c {
parse_tf_attribute(cx, args)
}
}
pub(crate) struct SanitizeParser;
impl<S: Stage> SingleAttributeParser<S> 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<S> = OnDuplicate::Error;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
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 })
}
}

View file

@ -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<Symbol>,
@ -25,7 +18,7 @@ impl<S: Stage> AttributeParser<S> 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() {

View file

@ -0,0 +1,33 @@
use super::prelude::*;
pub(crate) struct CrateNameParser;
impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
const PATH: &[Symbol] = &[sym::crate_name];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = 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<AttributeKind> {
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,
})
}
}

View file

@ -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<S: Stage>(
@ -54,6 +51,8 @@ impl<S: Stage> SingleAttributeParser<S> 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 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
}
Some(name @ sym::suggestion) => {
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 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
} else if let Some(version) = parse_version(since) {
DeprecatedSince::RustcVersion(version)
} else {
cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
cx.emit_err(InvalidSince { span: cx.attr_span });
DeprecatedSince::Err
}
} else if is_rustc {
cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
cx.emit_err(MissingSince { span: cx.attr_span });
DeprecatedSince::Err
} else {
DeprecatedSince::Unspecified
};
if is_rustc && note.is_none() {
cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
cx.emit_err(MissingNote { span: cx.attr_span });
return None;
}

View file

@ -3,8 +3,10 @@ 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::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::target_checking::{ALL_TARGETS, AllowedTargets};
pub(crate) struct DummyParser;
impl<S: Stage> SingleAttributeParser<S> for DummyParser {
const PATH: &[Symbol] = &[sym::rustc_dummy];

View file

@ -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<S: Stage> SingleAttributeParser<S> for InlineParser {
@ -56,14 +49,14 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span))
}
_ => {
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 =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "inline");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;

View file

@ -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<S: Stage> SingleAttributeParser<S> for LinkNameParser {
@ -212,16 +206,16 @@ impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
_ => {
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;

View file

@ -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<S: Stage> NoArgsAttributeParser<S> for AsPtrParser {
const PATH: &[Symbol] = &[sym::rustc_as_ptr];

View file

@ -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<S: Stage> NoArgsAttributeParser<S> for LoopMatchParser {
const PATH: &[Symbol] = &[sym::loop_match];

View file

@ -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<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
const PATH: &[Symbol] = &[sym::macro_escape];
@ -107,8 +101,8 @@ impl<S: Stage> AttributeParser<S> 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(),

View file

@ -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_hir::attrs::AttributeKind;
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;
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 no_implicit_prelude;
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;

View file

@ -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<S: Stage> SingleAttributeParser<S> for MustUseParser {
const PATH: &[Symbol] = &[sym::must_use];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = 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 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
Some(value_str)
}
ArgParser::List(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
let suggestions = <Self as SingleAttributeParser<S>>::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(),

View file

@ -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<S: Stage> NoArgsAttributeParser<S> for NoImplicitPreludeParser {

View file

@ -3,8 +3,10 @@ use rustc_hir::attrs::AttributeKind;
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<S: Stage> NoArgsAttributeParser<S> for NonExhaustiveParser {

View file

@ -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<S: Stage> SingleAttributeParser<S> for PathParser {

View file

@ -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};

View file

@ -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<S: Stage> NoArgsAttributeParser<S> for ProcMacroParser {
const PATH: &[Symbol] = &[sym::proc_macro];
@ -110,7 +100,7 @@ fn parse_derive_like<S: Stage>(
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 {

View file

@ -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<S: Stage> SingleAttributeParser<S> for CustomMirParser {
const PATH: &[rustc_span::Symbol] = &[sym::custom_mir];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = 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<AttributeKind> {
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<S: Stage>(
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<S: Stage>(
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<S: Stage>(
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))
}

View file

@ -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

View file

@ -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<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {

View file

@ -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<S: Stage> NoArgsAttributeParser<S> for MayDangleParser {
const PATH: &[Symbol] = &[sym::may_dangle];

View file

@ -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 @@ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Static),
Allow(Target::ForeignFn),
Allow(Target::ForeignStatic),
Allow(Target::ExternCrate),
]);
#[derive(Default)]

View file

@ -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<S: Stage> SingleAttributeParser<S> for IgnoreParser {
@ -29,7 +21,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(false, "ignore");
.suggestions(cx.attr_style, "ignore");
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
@ -40,8 +32,8 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
Some(str_value)
}
ArgParser::List(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "ignore");
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "ignore");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
@ -89,7 +81,7 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
return None;
};
if !single.path().word_is(sym::expected) {
cx.expected_specific_argument_strings(list.span, vec!["expected"]);
cx.expected_specific_argument_strings(list.span, &[sym::expected]);
return None;
}
let Some(nv) = single.args().name_value() else {

View file

@ -1,16 +1,14 @@
use core::mem;
use rustc_feature::{AttributeTemplate, template};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::{MethodKind, Target};
use rustc_span::{Span, Symbol, sym};
use std::mem;
use super::prelude::*;
use crate::attributes::{
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
};
use crate::context::MaybeWarn::{Allow, Warn};
use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::target_checking::Policy::{Allow, Warn};
use crate::target_checking::{ALL_TARGETS, AllowedTargets};
pub(crate) struct SkipDuringMethodDispatchParser;
impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch];
@ -44,7 +42,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
Some(key @ sym::array) => (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;
}
};

View file

@ -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 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
Some(_) => {
cx.expected_specific_argument_strings(
nv.value_span,
vec!["transparent", "semitransparent", "opaque"],
&[sym::transparent, sym::semitransparent, sym::opaque],
);
None
}

View file

@ -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<Symbol> {
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<Item = &'tcx T>,
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<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser<'_>,
) -> Option<u128> {
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)
}

View file

@ -3,28 +3,28 @@ use std::collections::BTreeMap;
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::path::PathParser as PathAttributeParser;
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::traits::{
};
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<S> = LazyLock<GroupTypeInner<S>>;
struct GroupTypeInner<S: Stage> {
accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
finalizers: Vec<FinalizeFn<S>>,
pub(super) struct GroupTypeInner<S: Stage> {
pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
pub(super) finalizers: Vec<FinalizeFn<S>>,
}
struct GroupTypeInnerAccept<S: Stage> {
template: AttributeTemplate,
accept_fn: AcceptFn<S>,
allowed_targets: AllowedTargets,
pub(super) struct GroupTypeInnerAccept<S: Stage> {
pub(super) template: AttributeTemplate,
pub(super) accept_fn: AcceptFn<S>,
pub(super) allowed_targets: AllowedTargets,
}
type AcceptFn<S> =
@ -160,6 +159,7 @@ attribute_parsers!(
// tidy-alphabetical-start
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<ForceTargetFeatureParser>,
Combine<ReprParser>,
Combine<TargetFeatureParser>,
Combine<UnstableFeatureBoundParser>,
@ -167,6 +167,8 @@ attribute_parsers!(
// tidy-alphabetical-start
Single<CoverageParser>,
Single<CrateNameParser>,
Single<CustomMirParser>,
Single<DeprecationParser>,
Single<DummyParser>,
Single<ExportNameParser>,
@ -185,6 +187,7 @@ attribute_parsers!(
Single<RustcLayoutScalarValidRangeEnd>,
Single<RustcLayoutScalarValidRangeStart>,
Single<RustcObjectLifetimeDefaultParser>,
Single<SanitizeParser>,
Single<ShouldPanicParser>,
Single<SkipDuringMethodDispatchParser>,
Single<TransparencyParser>,
@ -262,11 +265,7 @@ impl Stage for Early {
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 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
/// 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 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
attr_style: self.attr_style,
})
}
@ -404,6 +407,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
attr_style: self.attr_style,
})
}
@ -414,6 +418,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedList,
attr_style: self.attr_style,
})
}
@ -424,6 +429,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNoArgs,
attr_style: self.attr_style,
})
}
@ -435,6 +441,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIdentifier,
attr_style: self.attr_style,
})
}
@ -447,6 +454,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNameValue(name),
attr_style: self.attr_style,
})
}
@ -458,6 +466,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::DuplicateKey(key),
attr_style: self.attr_style,
})
}
@ -470,6 +479,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::UnexpectedLiteral,
attr_style: self.attr_style,
})
}
@ -480,6 +490,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSingleArgument,
attr_style: self.attr_style,
})
}
@ -490,13 +501,15 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
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 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
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 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
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 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
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<S::Id>),
pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
}
/// 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<Target> {
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<Symbol>,
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<Symbol>,
}
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<Attribute> {
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<T>(
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<Symbol>,
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<S::Id>),
) -> Vec<Attribute> {
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::<Vec<_>>();
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: 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>,
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<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser<'_>,
) -> Option<u128> {
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)
}

View file

@ -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<Symbol>,
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<Symbol>,
}
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<Attribute> {
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<Attribute> {
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<Symbol>,
target: Target,
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
emit_errors: ShouldEmit,
) -> Vec<Attribute> {
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<T>(
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<T>,
template: &AttributeTemplate,
) -> Option<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 parts =
normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
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<Symbol>,
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<S::Id>),
) -> Vec<Attribute> {
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::<Vec<_>>();
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 }
}
}
}
}

View file

@ -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" }

View file

@ -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<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emitter: L) {
pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emitter: L) {
let AttributeLint { id, span, kind } = lint;
match kind {
@ -35,12 +37,12 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, 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<L: LintEmitter>(lint: &AttributeLint<HirId>, 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,
},
),
}

View file

@ -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<Self::Item> {
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 @@ impl<'a> PathParser<'a> {
}
pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
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 @@ impl<'a> PathParser<'a> {
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 @@ impl<'a> ArgParser<'a> {
}
}
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<Self> {
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 @@ impl<'a> Debug for MetaItemParser<'a> {
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<Self> {
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 @@ impl NameValueParser {
}
}
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<MetaItemLit> {
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<TokenStreamIter<'a>>,
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<AttrPath> {
// 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<MetaItemLit> {
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( <insides> )]`
///
/// 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<MetaItemOrLitParser<'a>> {
// 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<MetaItemOrLitParser<'a>>,
sub_parsers: ThinVec<MetaItemOrLitParser<'a>>,
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<Self> {
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

View file

@ -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<Span>,
@ -568,7 +571,7 @@ pub(crate) enum AttributeParseErrorReason {
ExpectedNameValue(Option<Symbol>),
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 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
list: false,
} => {
let quote = if strings { '"' } else { '`' };
match possibilities.as_slice() {
match possibilities {
&[] => {}
&[x] => {
diag.span_label(
@ -683,7 +687,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
list: true,
} => {
let quote = if strings { '"' } else { '`' };
match possibilities.as_slice() {
match possibilities {
&[] => {}
&[x] => {
diag.span_label(
@ -717,7 +721,8 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
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 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
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<InvalidMetaItemQuoteIdentSugg>,
#[subdiagnostic]
pub remove_neg_sugg: Option<InvalidMetaItemRemoveNegSugg>,
}
#[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,
}

View file

@ -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<Target> {
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<S: Stage> 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<S::Id>),
) {
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: Target,
features: Option<&Features>,
) -> (Vec<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));
}
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>,
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 }),
]
};

View file

@ -8,16 +8,16 @@ use rustc_ast::{
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::<Late>::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::<Late>::is_parsed_attribute(slice::from_ref(&name)) {
return;
}
emit_malformed_attribute(psess, style, meta.span, name, template);
}

View file

@ -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

View file

@ -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 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
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 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
/// 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];

View file

@ -6,7 +6,9 @@ use rustc_abi::{FieldIdx, VariantIdx};
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// 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<G: EmissionGuarantee>(
&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;
}
}
}
}
}

View file

@ -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::bug;
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<Location> {
use rustc_middle::mir::visit::Visitor;
struct FindLocalAssignmentVisitor {
needle: Local,
locations: Vec<Location>,
}
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
// 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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
// 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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
);
}
// 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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
} 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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
/// locations.
fn find_assignments(&self, local: Local) -> Vec<Location> {
use rustc_middle::mir::visit::Visitor;
struct FindLocalAssignmentVisitor {
needle: Local,
locations: Vec<Location>,
}
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
..
})) => {
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::<Vec<_>>()
.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::<Vec<_>>()
.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<Span>,
opt_ty_info: Option<Span>,
infcx: &crate::BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
) -> Option<AmpMutSugg> {
// 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`

View file

@ -13,6 +13,8 @@ use rustc_middle::mir::{self, ConstraintCategory, Location};
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if errors.is_empty() {
return;
}
let infcx = self.infcx;
let mut guar = None;
let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, 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 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
// 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
})
{

View file

@ -23,7 +23,6 @@ use rustc_trait_selection::error_reporting::infer::nice_region_error::{
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
.collect::<Vec<_>>();
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
};
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, '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,

View file

@ -15,7 +15,6 @@ use tracing::debug;
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<RegionVid, ConstraintSccIndex>,
pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
pub(crate) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
pub(crate) outlives_constraints: Frozen<OutlivesConstraintSet<'tcx>>,
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
pub(crate) liveness_constraints: LivenessValues,
@ -147,9 +145,10 @@ impl scc::Annotation for RegionTracker {
/// 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<IndexVec<RegionVid, RegionDefinition<'tcx>>>, 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,

View file

@ -78,7 +78,6 @@ mod dataflow;
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,
&region_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, &regioncx, &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<FxIndexMap<ty::RegionVid, RegionCtxt>>,
pub(crate) root_def_id: LocalDefId,
pub(crate) param_env: ParamEnv<'tcx>,
pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
}
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 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
};
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<F>(

View file

@ -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<R, NllMemberConstraintIndex>,
/// 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<NllMemberConstraintIndex, MemberConstraint<'tcx>>,
/// 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<ty::RegionVid>,
}
/// Represents a `R0 member of [R1..Rn]` constraint
#[derive(Debug)]
pub(crate) struct MemberConstraint<'tcx> {
next_constraint: Option<NllMemberConstraintIndex>,
/// 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<R2>(
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<Item = NllMemberConstraintIndex> {
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<Item = NllMemberConstraintIndex> {
let mut next = self.first_constraints.get(&member_region_vid).cloned();
std::iter::from_fn(move || -> Option<NllMemberConstraintIndex> {
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<NllMemberConstraintIndex> 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<NllMemberConstraintIndex, MemberConstraint<'_>>,
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;
}
}
}
}

View file

@ -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::hygiene::DesugaringKind;
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<ConstraintSccIndex, RegionTracker>,
/// 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<ReverseSccGraph>,
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
/// 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<AppliedMemberConstraint>,
/// Map universe indexes to information on why we created it.
universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
@ -150,32 +132,6 @@ pub struct RegionInferenceContext<'tcx> {
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
}
/// 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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
liveness_constraints,
universe_causes,
placeholder_indices,
member_constraints,
} = lowered_constraints;
debug!("universal_regions: {:#?}", universal_region_relations.universal_regions);
@ -385,9 +339,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
});
// 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<ty::RegionVid> = 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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
#[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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
/// 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<OutlivesConstraint<'tcx>>, 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<OutlivesConstraint<'tcx>>, RegionVid)> {
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
context[from_region] = Trace::StartRegion;
@ -1677,7 +1452,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// `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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// 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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
) -> (BlameConstraint<'tcx>, Vec<OutlivesConstraint<'tcx>>) {
// 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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&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.
///

View file

@ -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<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
let mut errors = Vec::new();
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (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<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
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<Ty<'tcx>, 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)
}
}

View file

@ -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::<Vec<_>>();
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<ConstraintSccIndex, Vec<&'b DefiningUse<'tcx>>>,
}
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<TyCtxt<'tcx>> 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),
}
}
}

View file

@ -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<UniversalRegionRelations<'tcx>>,
region_bound_pairs: &RegionBoundPairs<'tcx>,
known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
location_map: &Rc<DenseLocationMap>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
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::<Vec<_>>();
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<Region<'tcx>> {
// 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<RegionVid>,
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<UniversalRegionRelations<'tcx>>,
location_map: Rc<DenseLocationMap>,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
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<DeferredOpaqueTypeError<'tcx>>,
) -> Vec<DefiningUse<'tcx>> {
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<DeferredOpaqueTypeError<'tcx>>,
) {
let infcx = rcx.infcx;
let tcx = infcx.tcx;
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (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<GenericArgsRef<'tcx>, 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<TyCtxt<'tcx>> 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<Region<'tcx>, 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<Ty<'tcx>, 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<DeferredOpaqueTypeError<'tcx>> {
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<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
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<Ty<'tcx>, 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)
}
}

View file

@ -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<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
pub(super) universal_region_relations: &'a UniversalRegionRelations<'tcx>,
pub(super) constraint_sccs: ConstraintSccs,
pub(super) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
pub(super) rev_scc_graph: ReverseSccGraph,
pub(super) scc_values: RegionValues<ConstraintSccIndex>,
}
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<UniversalRegionRelations<'tcx>>,
location_map: Rc<DenseLocationMap>,
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)
}
}

View file

@ -5,7 +5,6 @@ use rustc_data_structures::graph;
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 @@ impl ReverseSccGraph {
.filter(move |r| duplicates.insert(*r))
}
}
impl RegionInferenceContext<'_> {
/// 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())
})
}
}

View file

@ -2,7 +2,7 @@ use rustc_abi::FieldIdx;
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<ErrorGuaranteed>,
/// This should be `None` during normal compilation. See [`crate::consumers`] for more
/// information on how this is used.
pub(crate) consumer: Option<BorrowckConsumer<'tcx>>,
pub consumer: Option<BorrowckConsumer<'tcx>>,
}
impl<'tcx> BorrowCheckRootCtxt<'tcx> {
@ -38,6 +38,10 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
}
}
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 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
}
}
pub(super) fn get_concrete_opaque_type(
&mut self,
def_id: LocalDefId,
) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> {
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);
}

View file

@ -2,8 +2,9 @@ use std::fmt;
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 rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
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<R, ErrorGuaranteed>
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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
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<T>(

View file

@ -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::constraints::OutlivesConstraint;
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 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
infcx,
universal_regions,
region_bound_pairs,
param_env,
known_type_outlives_obligations,
locations,
span,
@ -286,8 +285,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
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 {

View file

@ -2,9 +2,9 @@ use rustc_data_structures::frozen::Frozen;
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 rustc_trait_selection::traits::query::type_op::{self, TypeOp};
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 @@ impl UniversalRegionRelations<'_> {
}
struct UniversalRegionRelationsBuilder<'a, 'tcx> {
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 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, '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 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, '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 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, '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 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
self.infcx,
&self.universal_regions,
&self.region_bound_pairs,
param_env,
&known_type_outlives_obligations,
Locations::All(span),
span,
@ -353,10 +351,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
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 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
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);

View file

@ -640,7 +640,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> {
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 }
}

View file

@ -26,8 +26,7 @@ use rustc_middle::ty::adjustment::PointerCoercion;
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 tracing::{debug, instrument, trace};
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<UniversalRegionRelations<'tcx>>,
pub(crate) opaque_type_values: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
pub(crate) region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
pub(crate) known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
pub(crate) polonius_context: Option<PoloniusContext>,
}
@ -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<ty::UniverseIndex, UniverseInfo<'tcx>>,
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
@ -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 @@ impl<'a, 'tcx> TypeChecker<'a, '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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
.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<TypeOpOutput<'tcx, Self>, 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)

View file

@ -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<OpaqueTypeKey<'tcx>, 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<TyCtxt<'tcx>> 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);
}
}
}
}

View file

@ -124,8 +124,13 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, '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 {

View file

@ -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;

View file

@ -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};

View file

@ -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<ast::Expr> = match substructure.fields {
SubstructureFields::StaticStruct(variant, _) => match variant {
// Self {
// field: value
// }
// Self { field: value }
VariantData::Struct { .. } => cx.expr_struct_ident(
span,
self_kw,

View file

@ -10,11 +10,12 @@ use rustc_ast::{
};
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 @@ fn make_format_args(
named_arg_sp: arg_name.span,
named_arg_name: arg_name.name.to_string(),
is_formatting_arg: matches!(used_as, Width | Precision),
},
}
.into(),
});
}
}

View file

@ -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)]

View file

@ -141,7 +141,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
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);

View file

@ -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};

View file

@ -42,12 +42,13 @@ trait ArgAttributesExt {
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 get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
}
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));
}
}

View file

@ -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));
}

View file

@ -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<Addition>,
}
enum Addition {
File { path: PathBuf, name_in_archive: String },
Archive { path: PathBuf, archive: ArchiveRO, skip: Box<dyn FnMut(&str) -> 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<dyn FnMut(&str) -> 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<Self>, 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<dyn ArchiveBuilder + 'a> {
// 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<bool> {
let kind = &*self.sess.target.archive_format;
let kind = kind
.parse::<ArchiveKind>()
.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}"))
}

View file

@ -0,0 +1,5 @@
pub(crate) mod archive;
pub(crate) mod lto;
pub(crate) mod owned_target_machine;
mod profiling;
pub(crate) mod write;

View file

@ -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 @@ impl OwnedTargetMachine {
args_cstr_buff: &[u8],
use_wasm_eh: bool,
) -> Result<Self, LlvmError<'static>> {
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 @@ impl OwnedTargetMachine {
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 @@ impl Drop for OwnedTargetMachine {
// 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());
}
}
}

View file

@ -1453,7 +1453,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
instance: Option<Instance<'tcx>>,
) {
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(),

View file

@ -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

View file

@ -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<Box<Recursive>>,
// }
// ```
// It can produce an ADT stack like this,
// - Box<Recursive>
// - Recursive
// - Box<Box<Recursive>>
&& 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);

View file

@ -330,10 +330,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
_ => 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 => {

View file

@ -46,18 +46,11 @@ use rustc_session::Session;
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;

View file

@ -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<ArchiveRO, String> {
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<Child<'a>, String>;
fn next(&mut self) -> Option<Result<Child<'a>, 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 _));
}
}
}

View file

@ -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 @@ impl RealPredicate {
}
}
/// LLVMTypeKind
#[derive(Copy, Clone, PartialEq, Debug)]
/// Must match the layout of `LLVMTypeKind`.
///
/// Use [`RawEnum<TypeKind>`] 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 @@ pub(crate) struct Builder<'a>(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 @@ unsafe extern "C" {
CanThrow: llvm::Bool,
) -> &'ll Value;
pub(crate) safe fn LLVMGetTypeKind(Ty: &Type) -> RawEnum<TypeKind>;
// Operations on integer types
pub(crate) fn LLVMInt1TypeInContext(C: &Context) -> &Type;
pub(crate) fn LLVMInt8TypeInContext(C: &Context) -> &Type;
@ -1197,7 +1188,7 @@ unsafe extern "C" {
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 @@ unsafe extern "C" {
// 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 @@ unsafe extern "C" {
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 @@ unsafe extern "C" {
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 @@ unsafe extern "C" {
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);

View file

@ -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::MetadataType::*;
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<Self, Self::Err> {
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);

View file

@ -277,6 +277,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
{
None
}
("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None,
// Filter out features that are not supported by the current LLVM version
("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None,
(

View file

@ -204,7 +204,7 @@ impl<'ll, CX: Borrow<SCx<'ll>>> BaseTypeCodegenMethods for GenericCx<'ll, CX> {
}
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 {

View file

@ -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

View file

@ -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, _))

Some files were not shown because too many files have changed in this diff Show more