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:
commit
721337b92a
1775 changed files with 34974 additions and 19179 deletions
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
294
Cargo.lock
294
Cargo.lock
|
|
@ -152,9 +152,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.98"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
|
||||
[[package]]
|
||||
name = "ar_archive_writer"
|
||||
|
|
@ -204,7 +204,7 @@ dependencies = [
|
|||
"rustc-hash 2.1.1",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -266,9 +266,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
version = "2.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29"
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
|
|
@ -315,7 +315,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"sha2",
|
||||
"tar",
|
||||
"toml 0.5.11",
|
||||
"toml 0.7.8",
|
||||
"xz2",
|
||||
]
|
||||
|
||||
|
|
@ -336,7 +336,7 @@ dependencies = [
|
|||
"curl",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"toml 0.5.11",
|
||||
"toml 0.7.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -421,7 +421,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde-untagged",
|
||||
"serde-value",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.15",
|
||||
"toml 0.8.23",
|
||||
"unicode-xid",
|
||||
"url",
|
||||
|
|
@ -453,7 +453,7 @@ dependencies = [
|
|||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -518,9 +518,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.43"
|
||||
version = "4.5.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f"
|
||||
checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
|
@ -538,9 +538,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.43"
|
||||
version = "4.5.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65"
|
||||
checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -550,14 +550,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.41"
|
||||
version = "4.5.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
|
||||
checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -720,7 +720,7 @@ dependencies = [
|
|||
"nom",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -907,9 +907,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "curl"
|
||||
version = "0.4.48"
|
||||
version = "0.4.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e2d5c8f48d9c0c23250e52b55e82a6ab4fdba6650c931f5a0a57a43abda812b"
|
||||
checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc"
|
||||
dependencies = [
|
||||
"curl-sys",
|
||||
"libc",
|
||||
|
|
@ -922,9 +922,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "curl-sys"
|
||||
version = "0.4.82+curl-8.14.1"
|
||||
version = "0.4.83+curl-8.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4d63638b5ec65f1a4ae945287b3fd035be4554bbaf211901159c9a2a74fb5be"
|
||||
checksum = "5830daf304027db10c82632a464879d46a3f7c4ba17a31592657ad16c719b483"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
|
@ -937,9 +937,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.166"
|
||||
version = "1.0.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5287274dfdf7e7eaa3d97d460eb2a94922539e6af214bda423f292105011ee2"
|
||||
checksum = "7aa144b12f11741f0dab5b4182896afad46faa0598b6a061f7b9d17a21837ba7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-cmd",
|
||||
|
|
@ -951,9 +951,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.166"
|
||||
version = "1.0.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65f3ce027a744135db10a1ebffa0863dab685aeef48f40a02c201f5e70c667d3"
|
||||
checksum = "12d3cbb84fb003242941c231b45ca9417e786e66e94baa39584bd99df3a270b6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
|
|
@ -961,40 +961,40 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-cmd"
|
||||
version = "1.0.166"
|
||||
version = "1.0.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a07dc23f2eea4774297f4c9a17ae4065fecb63127da556e6c9fadb0216d93595"
|
||||
checksum = "3fa36b7b249d43f67a3f54bd65788e35e7afe64bbc671396387a48b3e8aaea94"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.166"
|
||||
version = "1.0.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7a4dbad6171f763c4066c83dcd27546b6e93c5c5ae2229f9813bda7233f571d"
|
||||
checksum = "77707c70f6563edc5429618ca34a07241b75ebab35bd01d46697c75d58f8ddfe"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.166"
|
||||
version = "1.0.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9be4b527950fc42db06163705e78e73eedc8fd723708e942afe3572a9a2c366"
|
||||
checksum = "ede6c0fb7e318f0a11799b86ee29dcf17b9be2960bd379a6c38e1a96a6010fff"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1018,7 +1018,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1029,7 +1029,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
|||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1061,7 +1061,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1082,7 +1082,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1092,7 +1092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1104,7 +1104,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1129,16 +1129,16 @@ version = "6.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
|
||||
dependencies = [
|
||||
"dirs-sys 0.5.0",
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys 0.4.1",
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1151,18 +1151,6 @@ dependencies = [
|
|||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.4.6",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.5.0"
|
||||
|
|
@ -1194,7 +1182,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1379,7 +1367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1539,9 +1527,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
|
|
@ -1855,7 +1843,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2044,7 +2032,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2102,7 +2090,7 @@ dependencies = [
|
|||
"pest_derive",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2337,7 +2325,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2643,9 +2631,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.37.2"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3e3d0a7419f081f4a808147e845310313a39f322d7ae1f996b7f001d6cbed04"
|
||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
|
|
@ -2653,7 +2641,7 @@ dependencies = [
|
|||
"indexmap",
|
||||
"memchr",
|
||||
"ruzstd 0.8.1",
|
||||
"wasmparser 0.236.0",
|
||||
"wasmparser 0.236.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2834,7 +2822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.15",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
|
|
@ -2858,7 +2846,7 @@ dependencies = [
|
|||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3007,9 +2995,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
|
@ -3147,9 +3135,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
|
|
@ -3157,9 +3145,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
|
|
@ -3193,7 +3181,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
|||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3279,11 +3267,11 @@ dependencies = [
|
|||
"build_helper",
|
||||
"gimli 0.32.0",
|
||||
"libc",
|
||||
"object 0.37.2",
|
||||
"object 0.37.3",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"similar",
|
||||
"wasmparser 0.236.0",
|
||||
"wasmparser 0.236.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3367,6 +3355,7 @@ dependencies = [
|
|||
"rand 0.9.2",
|
||||
"rand_xoshiro",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_messages",
|
||||
"rustc_hashes",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
|
|
@ -3458,7 +3447,6 @@ dependencies = [
|
|||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_macros",
|
||||
"rustc_parse",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
|
|
@ -3480,7 +3468,6 @@ dependencies = [
|
|||
name = "rustc_attr_parsing"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
|
|
@ -3490,6 +3477,7 @@ dependencies = [
|
|||
"rustc_hir",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
"rustc_parse",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"thin-vec",
|
||||
|
|
@ -3570,7 +3558,7 @@ dependencies = [
|
|||
"itertools",
|
||||
"libc",
|
||||
"measureme",
|
||||
"object 0.37.2",
|
||||
"object 0.37.3",
|
||||
"rustc-demangle",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
|
|
@ -3608,7 +3596,7 @@ dependencies = [
|
|||
"cc",
|
||||
"itertools",
|
||||
"libc",
|
||||
"object 0.37.2",
|
||||
"object 0.37.3",
|
||||
"pathdiff",
|
||||
"regex",
|
||||
"rustc_abi",
|
||||
|
|
@ -3779,6 +3767,8 @@ dependencies = [
|
|||
"icu_locid",
|
||||
"icu_provider_adapters",
|
||||
"intl-memoizer",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_baked_icu_data",
|
||||
"rustc_data_structures",
|
||||
"rustc_macros",
|
||||
|
|
@ -3796,21 +3786,17 @@ dependencies = [
|
|||
"derive_setters",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_codes",
|
||||
"rustc_error_messages",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hashes",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_lexer",
|
||||
"rustc_lint_defs",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"rustc_type_ir",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"termcolor",
|
||||
|
|
@ -3865,7 +3851,7 @@ dependencies = [
|
|||
"fluent-syntax",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
|
|
@ -3898,7 +3884,9 @@ dependencies = [
|
|||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_messages",
|
||||
"rustc_hashes",
|
||||
"rustc_hir_id",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
|
|
@ -3936,6 +3924,17 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_hir_id"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_data_structures",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_span",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_hir_pretty"
|
||||
version = "0.0.0"
|
||||
|
|
@ -4012,7 +4011,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4123,11 +4122,10 @@ dependencies = [
|
|||
name = "rustc_lint_defs"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_messages",
|
||||
"rustc_hir",
|
||||
"rustc_hir_id",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_span",
|
||||
|
|
@ -4158,7 +4156,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -4268,7 +4266,6 @@ dependencies = [
|
|||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_graphviz",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
|
|
@ -4345,7 +4342,6 @@ dependencies = [
|
|||
"rustc-literal-escaper",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
|
|
@ -4644,9 +4640,10 @@ name = "rustc_target"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"object 0.37.2",
|
||||
"object 0.37.3",
|
||||
"rustc_abi",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_messages",
|
||||
"rustc_fs_util",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
|
|
@ -4710,7 +4707,6 @@ name = "rustc_traits"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_data_structures",
|
||||
"rustc_hir",
|
||||
"rustc_infer",
|
||||
"rustc_middle",
|
||||
"rustc_span",
|
||||
|
|
@ -4765,6 +4761,7 @@ dependencies = [
|
|||
"rustc-hash 2.1.1",
|
||||
"rustc_ast_ir",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_messages",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
|
|
@ -4781,7 +4778,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -4812,6 +4809,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"stringdex",
|
||||
"tempfile",
|
||||
"threadpool",
|
||||
"tracing",
|
||||
|
|
@ -4872,7 +4870,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5009,9 +5007,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde-untagged"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e"
|
||||
checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3"
|
||||
dependencies = [
|
||||
"erased-serde",
|
||||
"serde",
|
||||
|
|
@ -5036,7 +5034,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5127,12 +5125,12 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.10"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
||||
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5225,6 +5223,15 @@ dependencies = [
|
|||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stringdex"
|
||||
version = "0.0.1-alpha4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0"
|
||||
dependencies = [
|
||||
"stacker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
|
|
@ -5263,9 +5270,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -5280,7 +5287,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5398,11 +5405,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
version = "2.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.12",
|
||||
"thiserror-impl 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5413,18 +5420,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
version = "2.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5525,15 +5532,6 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.7.8"
|
||||
|
|
@ -5621,7 +5619,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5804,7 +5802,7 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5"
|
|||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"unic-langid-impl",
|
||||
]
|
||||
|
||||
|
|
@ -5936,9 +5934,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.17.0"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
||||
checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"js-sys",
|
||||
|
|
@ -6016,7 +6014,7 @@ dependencies = [
|
|||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
|
@ -6038,7 +6036,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
|
@ -6101,12 +6099,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.236.0"
|
||||
version = "0.236.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3108979166ab0d3c7262d2e16a2190ffe784b2a5beb963edef154b5e8e07680b"
|
||||
checksum = "724fccfd4f3c24b7e589d333fc0429c68042897a7e8a5f8694f31792471841e7"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser 0.236.0",
|
||||
"wasmparser 0.236.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6146,9 +6144,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.236.0"
|
||||
version = "0.236.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16d1eee846a705f6f3cb9d7b9f79b54583810f1fb57a1e3aea76d1742db2e3d2"
|
||||
checksum = "a9b1e81f3eb254cf7404a82cee6926a4a3ccc5aad80cc3d43608a070c67aa1d7"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
|
|
@ -6157,22 +6155,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "236.0.0"
|
||||
version = "236.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d6b6faeab519ba6fbf9b26add41617ca6f5553f99ebc33d876e591d2f4f3c6"
|
||||
checksum = "d3bec4b4db9c6808d394632fd4b0cd4654c32c540bd3237f55ee6a40fff6e51f"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"leb128fmt",
|
||||
"memchr",
|
||||
"unicode-width 0.2.1",
|
||||
"wasm-encoder 0.236.0",
|
||||
"wasm-encoder 0.236.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.236.0"
|
||||
version = "1.236.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc31704322400f461f7f31a5f9190d5488aaeafb63ae69ad2b5888d2704dcb08"
|
||||
checksum = "64475e2f77d6071ce90624098fc236285ddafa8c3ea1fb386f2c4154b6c2bbdb"
|
||||
dependencies = [
|
||||
"wast",
|
||||
]
|
||||
|
|
@ -6306,7 +6304,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6317,7 +6315,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6328,7 +6326,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6339,7 +6337,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6772,7 +6770,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6784,7 +6782,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6805,7 +6803,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6825,7 +6823,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6870,7 +6868,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6881,5 +6879,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
33
compiler/rustc_attr_parsing/src/attributes/crate_level.rs
Normal file
33
compiler/rustc_attr_parsing/src/attributes/crate_level.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
20
compiler/rustc_attr_parsing/src/attributes/prelude.rs
Normal file
20
compiler/rustc_attr_parsing/src/attributes/prelude.rs
Normal 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};
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
141
compiler/rustc_attr_parsing/src/attributes/prototype.rs
Normal file
141
compiler/rustc_attr_parsing/src/attributes/prototype.rs
Normal 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))
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
370
compiler/rustc_attr_parsing/src/interface.rs
Normal file
370
compiler/rustc_attr_parsing/src/interface.rs
Normal 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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
247
compiler/rustc_attr_parsing/src/target_checking.rs
Normal file
247
compiler/rustc_attr_parsing/src/target_checking.rs
Normal 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 }),
|
||||
]
|
||||
};
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
®ion_bound_pairs,
|
||||
&known_type_outlives_obligations,
|
||||
&location_map,
|
||||
&mut constraints,
|
||||
);
|
||||
|
||||
// Compute non-lexical lifetimes using the constraints computed
|
||||
// by typechecking the MIR body.
|
||||
let nll::NllOutput {
|
||||
|
|
@ -375,8 +386,6 @@ fn do_mir_borrowck<'tcx>(
|
|||
polonius_context,
|
||||
);
|
||||
|
||||
let opaque_type_errors = regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values);
|
||||
|
||||
// Dump MIR results into a file, if that is enabled. This lets us
|
||||
// write unit-tests, as well as helping with debugging.
|
||||
nll::dump_nll_mir(&infcx, body, ®ioncx, &opt_closure_req, &borrow_set);
|
||||
|
|
@ -581,12 +590,13 @@ fn get_flow_results<'a, 'tcx>(
|
|||
|
||||
pub(crate) struct BorrowckInferCtxt<'tcx> {
|
||||
pub(crate) infcx: InferCtxt<'tcx>,
|
||||
pub(crate) reg_var_to_origin: RefCell<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>(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
698
compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
Normal file
698
compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}"))
|
||||
}
|
||||
|
|
|
|||
5
compiler/rustc_codegen_llvm/src/back/mod.rs
Normal file
5
compiler/rustc_codegen_llvm/src/back/mod.rs
Normal 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;
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue