Merge pull request #4100 from rust-lang/rustup-2024-12-20
Automatic Rustup
This commit is contained in:
commit
db960a25d1
392 changed files with 6004 additions and 3924 deletions
245
Cargo.lock
245
Cargo.lock
|
|
@ -57,9 +57,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "ammonia"
|
||||
|
|
@ -101,12 +101,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "annotate-snippets"
|
||||
version = "0.11.4"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24e35ed54e5ea7997c14ed4c70ba043478db1112e98263b3b035907aa197d991"
|
||||
checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"unicode-width 0.1.14",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -182,9 +182,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.93"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
|
@ -285,9 +285,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22"
|
||||
checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata 0.4.9",
|
||||
|
|
@ -396,7 +396,7 @@ dependencies = [
|
|||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -426,9 +426,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
|
|
@ -470,9 +470,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.21"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
|
@ -490,9 +490,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.21"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -503,9 +503,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.38"
|
||||
version = "4.5.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01"
|
||||
checksum = "fd4db298d517d5fa00b2b84bbe044efd3fde43874a41db0d46f91994646a2da4"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
|
@ -524,9 +524,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
|
|
@ -683,12 +683,12 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
|||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -727,15 +727,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.8"
|
||||
version = "0.15.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"unicode-width 0.1.14",
|
||||
"windows-sys 0.52.0",
|
||||
"once_cell",
|
||||
"unicode-width 0.2.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -782,18 +782,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.13"
|
||||
version = "0.5.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
|
|
@ -810,9 +810,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.20"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
|
|
@ -1101,9 +1101,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
|
|
@ -1179,9 +1179,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
|||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "field-offset"
|
||||
|
|
@ -1212,7 +1212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide 0.8.0",
|
||||
"miniz_oxide 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1246,7 +1246,7 @@ version = "0.11.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1393,7 +1393,7 @@ dependencies = [
|
|||
"rinja",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1501,7 +1501,7 @@ dependencies = [
|
|||
"pest_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1550,11 +1550,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1942,9 +1942,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.74"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705"
|
||||
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
|
|
@ -2012,9 +2012,9 @@ checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.167"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libdbus-sys"
|
||||
|
|
@ -2123,7 +2123,7 @@ version = "0.0.1"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
|
@ -2305,9 +2305,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
checksum = "a2ef2593ffb6958c941575cee70c8e257438749971869c4ae5acf6f91a168a61"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
|
@ -2701,20 +2701,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.7.14"
|
||||
version = "2.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442"
|
||||
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror",
|
||||
"thiserror 2.0.7",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.7.14"
|
||||
version = "2.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd"
|
||||
checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
|
|
@ -2722,9 +2722,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.7.14"
|
||||
version = "2.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e"
|
||||
checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
|
|
@ -2735,9 +2735,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.7.14"
|
||||
version = "2.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d"
|
||||
checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
|
|
@ -3020,9 +3020,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
|
@ -3035,7 +3035,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
|||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3249,9 +3249,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc-stable-hash"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5c9f15eec8235d7cb775ee6f81891db79b98fd54ba1ad8fae565b88ef1ae4e2"
|
||||
checksum = "2febf9acc5ee5e99d1ad0afcdbccc02d87aa3f857a1f01f825b80eacf8edfcd1"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-std-workspace-alloc"
|
||||
|
|
@ -3711,7 +3711,7 @@ dependencies = [
|
|||
name = "rustc_errors"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.11.4",
|
||||
"annotate-snippets 0.11.5",
|
||||
"derive_setters",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
|
|
@ -3774,7 +3774,7 @@ dependencies = [
|
|||
name = "rustc_fluent_macro"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.11.4",
|
||||
"annotate-snippets 0.11.5",
|
||||
"fluent-bundle",
|
||||
"fluent-syntax",
|
||||
"proc-macro2",
|
||||
|
|
@ -4728,7 +4728,7 @@ checksum = "81864b097046da5df3758fdc6e4822bbb70afa06317e8ca45ea1b51cb8c5e5a4"
|
|||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
@ -4762,7 +4762,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"term",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"toml 0.7.8",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
|
|
@ -4773,15 +4773,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.41"
|
||||
version = "0.38.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4841,38 +4841,38 @@ version = "0.10.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d"
|
||||
dependencies = [
|
||||
"self_cell 1.0.4",
|
||||
"self_cell 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "1.0.4"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a"
|
||||
checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.215"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -5008,7 +5008,7 @@ checksum = "53d7ac03c67c572d85049d6db815e20a4a19b41b3d5cca732ac582342021ad77"
|
|||
dependencies = [
|
||||
"nom",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
@ -5025,7 +5025,7 @@ dependencies = [
|
|||
"spdx-expression",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
|
@ -5273,7 +5273,16 @@ version = "1.0.69"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5287,6 +5296,17 @@ dependencies = [
|
|||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thorin-dwp"
|
||||
version = "0.8.0"
|
||||
|
|
@ -5351,9 +5371,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
version = "0.3.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
|
|
@ -5372,9 +5392,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
|
|
@ -5407,9 +5427,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.41.1"
|
||||
version = "1.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
|
||||
checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
|
@ -5597,7 +5617,7 @@ version = "0.26.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32ee4c40e5a5f9fa6864ff976473e5d6a6e9884b6ce68b40690d9f87e1994c83"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.11.4",
|
||||
"annotate-snippets 0.11.5",
|
||||
"anyhow",
|
||||
"bstr",
|
||||
"cargo-platform",
|
||||
|
|
@ -5843,9 +5863,9 @@ checksum = "0f76d9fa52234153eeb40b088de91a8c13dc28a912cf6f31cd89ca4bac9024e0"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.97"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c"
|
||||
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
|
|
@ -5854,13 +5874,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.97"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd"
|
||||
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
|
|
@ -5869,9 +5888,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.97"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051"
|
||||
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
|
@ -5879,9 +5898,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.97"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d"
|
||||
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -5892,9 +5911,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.97"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
|
||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-component-ld"
|
||||
|
|
@ -5935,12 +5954,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.221.0"
|
||||
version = "0.221.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de35b6c3ef1f53ac7a31b5e69bc00f1542ea337e7e7162dc34c68b537ff82690"
|
||||
checksum = "c17a3bd88f2155da63a1f2fcb8a56377a24f0b6dfed12733bb5f544e86f690c5"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
"wasmparser 0.221.0",
|
||||
"wasmparser 0.221.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5984,9 +6003,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.221.0"
|
||||
version = "0.221.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8659e755615170cfe20da468865c989da78c5da16d8652e69a75acda02406a92"
|
||||
checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
|
|
@ -5995,22 +6014,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "221.0.0"
|
||||
version = "221.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d8eb1933d493dd07484a255c3f52236123333f5befaa3be36182a50d393ec54"
|
||||
checksum = "fcc4470b9de917ba199157d1f0ae104f2ae362be728c43e68c571c7715bd629e"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"leb128",
|
||||
"memchr",
|
||||
"unicode-width 0.2.0",
|
||||
"wasm-encoder 0.221.0",
|
||||
"wasm-encoder 0.221.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.221.0"
|
||||
version = "1.221.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c813fd4e5b2b97242830b56e7b7dc5479bc17aaa8730109be35e61909af83993"
|
||||
checksum = "6b1f3c6d82af47286494c6caea1d332037f5cbeeac82bbf5ef59cb8c201c466e"
|
||||
dependencies = [
|
||||
"wast",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
|
||||
|
||||
match &self.variants {
|
||||
abi::Variants::Single { .. } => {}
|
||||
abi::Variants::Single { .. } | abi::Variants::Empty => {}
|
||||
abi::Variants::Multiple { variants, .. } => {
|
||||
// Treat enum variants like union members.
|
||||
// HACK(eddyb) pretend the `enum` field (discriminant)
|
||||
|
|
|
|||
|
|
@ -213,8 +213,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
&self,
|
||||
) -> LayoutData<FieldIdx, VariantIdx> {
|
||||
let dl = self.cx.data_layout();
|
||||
// This is also used for uninhabited enums, so we use `Variants::Empty`.
|
||||
LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
variants: Variants::Empty,
|
||||
fields: FieldsShape::Primitive,
|
||||
backend_repr: BackendRepr::Uninhabited,
|
||||
largest_niche: None,
|
||||
|
|
@ -1004,8 +1005,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
|
||||
Variants::Multiple { tag, tag_encoding, tag_field, variants: best_layout.variants }
|
||||
}
|
||||
Variants::Single { .. } => {
|
||||
panic!("encountered a single-variant enum during multi-variant layout")
|
||||
Variants::Single { .. } | Variants::Empty => {
|
||||
panic!("encountered a single-variant or empty enum during multi-variant layout")
|
||||
}
|
||||
};
|
||||
Ok(best_layout.layout)
|
||||
|
|
|
|||
|
|
@ -1504,10 +1504,12 @@ impl BackendRepr {
|
|||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
|
||||
/// A type with no valid variants. Must be uninhabited.
|
||||
Empty,
|
||||
|
||||
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
|
||||
Single {
|
||||
/// Always 0 for non-enums/generators.
|
||||
/// For enums without a variant, this is an invalid index!
|
||||
/// Always `0` for types that cannot have multiple variants.
|
||||
index: VariantIdx,
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
//! Functions dealing with attributes and meta items.
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::iter;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use rustc_index::bit_set::GrowableBitSet;
|
||||
|
|
@ -16,7 +15,9 @@ use crate::ast::{
|
|||
};
|
||||
use crate::ptr::P;
|
||||
use crate::token::{self, CommentKind, Delimiter, Token};
|
||||
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenTree};
|
||||
use crate::tokenstream::{
|
||||
DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
|
||||
};
|
||||
use crate::util::comments;
|
||||
use crate::util::literal::escape_string_symbol;
|
||||
|
||||
|
|
@ -365,12 +366,9 @@ impl MetaItem {
|
|||
}
|
||||
}
|
||||
|
||||
fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
|
||||
where
|
||||
I: Iterator<Item = &'a TokenTree>,
|
||||
{
|
||||
fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItem> {
|
||||
// FIXME: Share code with `parse_path`.
|
||||
let tt = tokens.next().map(|tt| TokenTree::uninterpolate(tt));
|
||||
let tt = iter.next().map(|tt| TokenTree::uninterpolate(tt));
|
||||
let path = match tt.as_deref() {
|
||||
Some(&TokenTree::Token(
|
||||
Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
|
||||
|
|
@ -378,9 +376,9 @@ impl MetaItem {
|
|||
)) => 'arm: {
|
||||
let mut segments = if let &token::Ident(name, _) = kind {
|
||||
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
|
||||
tokens.peek()
|
||||
iter.peek()
|
||||
{
|
||||
tokens.next();
|
||||
iter.next();
|
||||
thin_vec![PathSegment::from_ident(Ident::new(name, span))]
|
||||
} else {
|
||||
break 'arm Path::from_ident(Ident::new(name, span));
|
||||
|
|
@ -390,16 +388,16 @@ impl MetaItem {
|
|||
};
|
||||
loop {
|
||||
if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
|
||||
tokens.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()
|
||||
iter.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()
|
||||
{
|
||||
segments.push(PathSegment::from_ident(Ident::new(name, span)));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
|
||||
tokens.peek()
|
||||
iter.peek()
|
||||
{
|
||||
tokens.next();
|
||||
iter.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
@ -420,8 +418,8 @@ impl MetaItem {
|
|||
}
|
||||
_ => return None,
|
||||
};
|
||||
let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
|
||||
let kind = MetaItemKind::from_tokens(tokens)?;
|
||||
let list_closing_paren_pos = iter.peek().map(|tt| tt.span().hi());
|
||||
let kind = MetaItemKind::from_tokens(iter)?;
|
||||
let hi = match &kind {
|
||||
MetaItemKind::NameValue(lit) => lit.span.hi(),
|
||||
MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
|
||||
|
|
@ -438,12 +436,12 @@ impl MetaItem {
|
|||
impl MetaItemKind {
|
||||
// public because it can be called in the hir
|
||||
pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {
|
||||
let mut tokens = tokens.trees().peekable();
|
||||
let mut iter = tokens.iter();
|
||||
let mut result = ThinVec::new();
|
||||
while tokens.peek().is_some() {
|
||||
let item = MetaItemInner::from_tokens(&mut tokens)?;
|
||||
while iter.peek().is_some() {
|
||||
let item = MetaItemInner::from_tokens(&mut iter)?;
|
||||
result.push(item);
|
||||
match tokens.next() {
|
||||
match iter.next() {
|
||||
None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
|
||||
_ => return None,
|
||||
}
|
||||
|
|
@ -451,12 +449,10 @@ impl MetaItemKind {
|
|||
Some(result)
|
||||
}
|
||||
|
||||
fn name_value_from_tokens<'a>(
|
||||
tokens: &mut impl Iterator<Item = &'a TokenTree>,
|
||||
) -> Option<MetaItemKind> {
|
||||
match tokens.next() {
|
||||
fn name_value_from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {
|
||||
match iter.next() {
|
||||
Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
|
||||
MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees())
|
||||
MetaItemKind::name_value_from_tokens(&mut inner_tokens.iter())
|
||||
}
|
||||
Some(TokenTree::Token(token, _)) => {
|
||||
MetaItemLit::from_token(token).map(MetaItemKind::NameValue)
|
||||
|
|
@ -465,19 +461,17 @@ impl MetaItemKind {
|
|||
}
|
||||
}
|
||||
|
||||
fn from_tokens<'a>(
|
||||
tokens: &mut iter::Peekable<impl Iterator<Item = &'a TokenTree>>,
|
||||
) -> Option<MetaItemKind> {
|
||||
match tokens.peek() {
|
||||
fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {
|
||||
match iter.peek() {
|
||||
Some(TokenTree::Delimited(.., Delimiter::Parenthesis, inner_tokens)) => {
|
||||
let inner_tokens = inner_tokens.clone();
|
||||
tokens.next();
|
||||
iter.next();
|
||||
MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
|
||||
}
|
||||
Some(TokenTree::Delimited(..)) => None,
|
||||
Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
|
||||
tokens.next();
|
||||
MetaItemKind::name_value_from_tokens(tokens)
|
||||
iter.next();
|
||||
MetaItemKind::name_value_from_tokens(iter)
|
||||
}
|
||||
_ => Some(MetaItemKind::Word),
|
||||
}
|
||||
|
|
@ -593,22 +587,19 @@ impl MetaItemInner {
|
|||
self.meta_item().is_some()
|
||||
}
|
||||
|
||||
fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemInner>
|
||||
where
|
||||
I: Iterator<Item = &'a TokenTree>,
|
||||
{
|
||||
match tokens.peek() {
|
||||
fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemInner> {
|
||||
match iter.peek() {
|
||||
Some(TokenTree::Token(token, _)) if let Some(lit) = MetaItemLit::from_token(token) => {
|
||||
tokens.next();
|
||||
iter.next();
|
||||
return Some(MetaItemInner::Lit(lit));
|
||||
}
|
||||
Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
|
||||
tokens.next();
|
||||
return MetaItemInner::from_tokens(&mut inner_tokens.trees().peekable());
|
||||
iter.next();
|
||||
return MetaItemInner::from_tokens(&mut inner_tokens.iter());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
MetaItem::from_tokens(tokens).map(MetaItemInner::MetaItem)
|
||||
MetaItem::from_tokens(iter).map(MetaItemInner::MetaItem)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -903,7 +903,8 @@ impl Token {
|
|||
self.is_non_raw_ident_where(|id| id.name == kw)
|
||||
}
|
||||
|
||||
/// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this token is an identifier equal to `kw` ignoring the case.
|
||||
/// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this
|
||||
/// token is an identifier equal to `kw` ignoring the case.
|
||||
pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool {
|
||||
self.is_keyword(kw)
|
||||
|| (case == Case::Insensitive
|
||||
|
|
@ -916,6 +917,11 @@ impl Token {
|
|||
self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
|
||||
}
|
||||
|
||||
/// Don't use this unless you're doing something very loose and heuristic-y.
|
||||
pub fn is_any_keyword(&self) -> bool {
|
||||
self.is_non_raw_ident_where(Ident::is_any_keyword)
|
||||
}
|
||||
|
||||
/// Returns true for reserved identifiers used internally for elided lifetimes,
|
||||
/// unnamed method parameters, crate root module, error recovery etc.
|
||||
pub fn is_special_ident(&self) -> bool {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ where
|
|||
CTX: crate::HashStableContext,
|
||||
{
|
||||
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
|
||||
for sub_tt in self.trees() {
|
||||
for sub_tt in self.iter() {
|
||||
sub_tt.hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
|
|
@ -406,7 +406,7 @@ impl Eq for TokenStream {}
|
|||
|
||||
impl PartialEq<TokenStream> for TokenStream {
|
||||
fn eq(&self, other: &TokenStream) -> bool {
|
||||
self.trees().eq(other.trees())
|
||||
self.iter().eq(other.iter())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -423,24 +423,24 @@ impl TokenStream {
|
|||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn trees(&self) -> RefTokenTreeCursor<'_> {
|
||||
RefTokenTreeCursor::new(self)
|
||||
pub fn get(&self, index: usize) -> Option<&TokenTree> {
|
||||
self.0.get(index)
|
||||
}
|
||||
|
||||
pub fn into_trees(self) -> TokenTreeCursor {
|
||||
TokenTreeCursor::new(self)
|
||||
pub fn iter(&self) -> TokenStreamIter<'_> {
|
||||
TokenStreamIter::new(self)
|
||||
}
|
||||
|
||||
/// Compares two `TokenStream`s, checking equality without regarding span information.
|
||||
pub fn eq_unspanned(&self, other: &TokenStream) -> bool {
|
||||
let mut t1 = self.trees();
|
||||
let mut t2 = other.trees();
|
||||
for (t1, t2) in iter::zip(&mut t1, &mut t2) {
|
||||
if !t1.eq_unspanned(t2) {
|
||||
let mut iter1 = self.iter();
|
||||
let mut iter2 = other.iter();
|
||||
for (tt1, tt2) in iter::zip(&mut iter1, &mut iter2) {
|
||||
if !tt1.eq_unspanned(tt2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
t1.next().is_none() && t2.next().is_none()
|
||||
iter1.next().is_none() && iter2.next().is_none()
|
||||
}
|
||||
|
||||
/// Create a token stream containing a single token with alone spacing. The
|
||||
|
|
@ -509,7 +509,7 @@ impl TokenStream {
|
|||
#[must_use]
|
||||
pub fn flattened(&self) -> TokenStream {
|
||||
fn can_skip(stream: &TokenStream) -> bool {
|
||||
stream.trees().all(|tree| match tree {
|
||||
stream.iter().all(|tree| match tree {
|
||||
TokenTree::Token(token, _) => !matches!(
|
||||
token.kind,
|
||||
token::NtIdent(..) | token::NtLifetime(..) | token::Interpolated(..)
|
||||
|
|
@ -522,7 +522,7 @@ impl TokenStream {
|
|||
return self.clone();
|
||||
}
|
||||
|
||||
self.trees().map(|tree| TokenStream::flatten_token_tree(tree)).collect()
|
||||
self.iter().map(|tree| TokenStream::flatten_token_tree(tree)).collect()
|
||||
}
|
||||
|
||||
// If `vec` is not empty, try to glue `tt` onto its last token. The return
|
||||
|
|
@ -665,25 +665,26 @@ impl TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
/// By-reference iterator over a [`TokenStream`], that produces `&TokenTree`
|
||||
/// items.
|
||||
#[derive(Clone)]
|
||||
pub struct RefTokenTreeCursor<'t> {
|
||||
pub struct TokenStreamIter<'t> {
|
||||
stream: &'t TokenStream,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'t> RefTokenTreeCursor<'t> {
|
||||
impl<'t> TokenStreamIter<'t> {
|
||||
fn new(stream: &'t TokenStream) -> Self {
|
||||
RefTokenTreeCursor { stream, index: 0 }
|
||||
TokenStreamIter { stream, index: 0 }
|
||||
}
|
||||
|
||||
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
|
||||
self.stream.0.get(self.index + n)
|
||||
// Peeking could be done via `Peekable`, but most iterators need peeking,
|
||||
// and this is simple and avoids the need to use `peekable` and `Peekable`
|
||||
// at all the use sites.
|
||||
pub fn peek(&self) -> Option<&'t TokenTree> {
|
||||
self.stream.0.get(self.index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> Iterator for RefTokenTreeCursor<'t> {
|
||||
impl<'t> Iterator for TokenStreamIter<'t> {
|
||||
type Item = &'t TokenTree;
|
||||
|
||||
fn next(&mut self) -> Option<&'t TokenTree> {
|
||||
|
|
@ -694,39 +695,6 @@ impl<'t> Iterator for RefTokenTreeCursor<'t> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Owning by-value iterator over a [`TokenStream`], that produces `&TokenTree`
|
||||
/// items.
|
||||
///
|
||||
/// Doesn't impl `Iterator` because Rust doesn't permit an owning iterator to
|
||||
/// return `&T` from `next`; the need for an explicit lifetime in the `Item`
|
||||
/// associated type gets in the way. Instead, use `next_ref` (which doesn't
|
||||
/// involve associated types) for getting individual elements, or
|
||||
/// `RefTokenTreeCursor` if you really want an `Iterator`, e.g. in a `for`
|
||||
/// loop.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TokenTreeCursor {
|
||||
pub stream: TokenStream,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl TokenTreeCursor {
|
||||
fn new(stream: TokenStream) -> Self {
|
||||
TokenTreeCursor { stream, index: 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn next_ref(&mut self) -> Option<&TokenTree> {
|
||||
self.stream.0.get(self.index).map(|tree| {
|
||||
self.index += 1;
|
||||
tree
|
||||
})
|
||||
}
|
||||
|
||||
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
|
||||
self.stream.0.get(self.index + n)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
pub struct DelimSpan {
|
||||
pub open: Span,
|
||||
|
|
|
|||
|
|
@ -725,7 +725,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
// E.g. we have seen cases where a proc macro can handle `a :: b` but not
|
||||
// `a::b`. See #117433 for some examples.
|
||||
fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
|
||||
let mut iter = tts.trees().peekable();
|
||||
let mut iter = tts.iter().peekable();
|
||||
while let Some(tt) = iter.next() {
|
||||
let spacing = self.print_tt(tt, convert_dollar_crate);
|
||||
if let Some(next) = iter.peek() {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use rustc_mir_dataflow::impls::{
|
|||
EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
|
||||
MaybeUninitializedPlacesDomain,
|
||||
};
|
||||
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects};
|
||||
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
|
||||
|
|
@ -101,16 +101,6 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
|
|||
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn apply_switch_int_edge_effects(
|
||||
&mut self,
|
||||
_block: BasicBlock,
|
||||
_discr: &mir::Operand<'tcx>,
|
||||
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
|
||||
) {
|
||||
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
impl JoinSemiLattice for BorrowckDomain {
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
/// Returns `true` if a closure is inferred to be an `FnMut` closure.
|
||||
fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
|
||||
if let Some(ty::ReLateParam(late_param)) = self.to_error_region(fr).as_deref()
|
||||
&& let ty::BoundRegionKind::ClosureEnv = late_param.bound_region
|
||||
&& let ty::LateParamRegionKind::ClosureEnv = late_param.kind
|
||||
&& let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty
|
||||
{
|
||||
return args.as_closure().kind() == ty::ClosureKind::FnMut;
|
||||
|
|
|
|||
|
|
@ -299,17 +299,17 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
Some(RegionName { name: kw::StaticLifetime, source: RegionNameSource::Static })
|
||||
}
|
||||
|
||||
ty::ReLateParam(late_param) => match late_param.bound_region {
|
||||
ty::BoundRegionKind::Named(region_def_id, name) => {
|
||||
ty::ReLateParam(late_param) => match late_param.kind {
|
||||
ty::LateParamRegionKind::Named(region_def_id, name) => {
|
||||
// Get the span to point to, even if we don't use the name.
|
||||
let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP);
|
||||
debug!(
|
||||
"bound region named: {:?}, is_named: {:?}",
|
||||
name,
|
||||
late_param.bound_region.is_named()
|
||||
late_param.kind.is_named()
|
||||
);
|
||||
|
||||
if late_param.bound_region.is_named() {
|
||||
if late_param.kind.is_named() {
|
||||
// A named region that is actually named.
|
||||
Some(RegionName {
|
||||
name,
|
||||
|
|
@ -331,7 +331,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
ty::BoundRegionKind::ClosureEnv => {
|
||||
ty::LateParamRegionKind::ClosureEnv => {
|
||||
let def_ty = self.regioncx.universal_regions().defining_ty;
|
||||
|
||||
let closure_kind = match def_ty {
|
||||
|
|
@ -368,7 +368,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
ty::BoundRegionKind::Anon => None,
|
||||
ty::LateParamRegionKind::Anon(_) => None,
|
||||
},
|
||||
|
||||
ty::ReBound(..)
|
||||
|
|
|
|||
|
|
@ -334,35 +334,7 @@ fn do_mir_borrowck<'tcx>(
|
|||
mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals);
|
||||
|
||||
debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
|
||||
let used_mut = std::mem::take(&mut mbcx.used_mut);
|
||||
for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) {
|
||||
let local_decl = &mbcx.body.local_decls[local];
|
||||
let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data {
|
||||
ClearCrossCrate::Set(data) => data.lint_root,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
// Skip over locals that begin with an underscore or have no name
|
||||
match mbcx.local_names[local] {
|
||||
Some(name) => {
|
||||
if name.as_str().starts_with('_') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
|
||||
let span = local_decl.source_info.span;
|
||||
if span.desugaring_kind().is_some() {
|
||||
// If the `mut` arises as part of a desugaring, we should ignore it.
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
|
||||
|
||||
tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span })
|
||||
}
|
||||
|
||||
mbcx.lint_unused_mut();
|
||||
let tainted_by_errors = mbcx.emit_errors();
|
||||
|
||||
let result = BorrowCheckResult {
|
||||
|
|
@ -2390,6 +2362,38 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
// `BasicBlocks` computes dominators on-demand and caches them.
|
||||
self.body.basic_blocks.dominators()
|
||||
}
|
||||
|
||||
fn lint_unused_mut(&self) {
|
||||
let tcx = self.infcx.tcx;
|
||||
let body = self.body;
|
||||
for local in body.mut_vars_and_args_iter().filter(|local| !self.used_mut.contains(local)) {
|
||||
let local_decl = &body.local_decls[local];
|
||||
let lint_root = match &body.source_scopes[local_decl.source_info.scope].local_data {
|
||||
ClearCrossCrate::Set(data) => data.lint_root,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
// Skip over locals that begin with an underscore or have no name
|
||||
match self.local_names[local] {
|
||||
Some(name) => {
|
||||
if name.as_str().starts_with('_') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
|
||||
let span = local_decl.source_info.span;
|
||||
if span.desugaring_kind().is_some() {
|
||||
// If the `mut` arises as part of a desugaring, we should ignore it.
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
|
||||
|
||||
tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod diags {
|
||||
|
|
|
|||
|
|
@ -2230,7 +2230,7 @@ impl<'tcx> RegionDefinition<'tcx> {
|
|||
fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self {
|
||||
// Create a new region definition. Note that, for free
|
||||
// regions, the `external_name` field gets updated later in
|
||||
// `init_universal_regions`.
|
||||
// `init_free_and_bound_regions`.
|
||||
|
||||
let origin = match rv_origin {
|
||||
RegionVariableOrigin::Nll(origin) => origin,
|
||||
|
|
|
|||
|
|
@ -842,8 +842,9 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
|
|||
{
|
||||
let (value, _map) = self.tcx.instantiate_bound_regions(value, |br| {
|
||||
debug!(?br);
|
||||
let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind);
|
||||
let liberated_region =
|
||||
ty::Region::new_late_param(self.tcx, all_outlive_scope.to_def_id(), br.kind);
|
||||
ty::Region::new_late_param(self.tcx, all_outlive_scope.to_def_id(), kind);
|
||||
let region_vid = {
|
||||
let name = match br.kind.get_name() {
|
||||
Some(name) => name,
|
||||
|
|
@ -941,12 +942,13 @@ fn for_each_late_bound_region_in_item<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
for bound_var in tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)) {
|
||||
let ty::BoundVariableKind::Region(bound_region) = bound_var else {
|
||||
continue;
|
||||
};
|
||||
let liberated_region =
|
||||
ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), bound_region);
|
||||
f(liberated_region);
|
||||
for (idx, bound_var) in
|
||||
tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)).iter().enumerate()
|
||||
{
|
||||
if let ty::BoundVariableKind::Region(kind) = bound_var {
|
||||
let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind);
|
||||
let liberated_region = ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), kind);
|
||||
f(liberated_region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
use ast::token::IdentIsRaw;
|
||||
use lint::BuiltinLintDiag;
|
||||
use rustc_ast::AsmMacro;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, Delimiter};
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{AsmMacro, token};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::*;
|
||||
use rustc_index::bit_set::GrowableBitSet;
|
||||
use rustc_parse::parser::Parser;
|
||||
use rustc_parse::exp;
|
||||
use rustc_parse::parser::{ExpKeywordPair, Parser};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::{ErrorGuaranteed, Ident, InnerSpan, Span, Symbol, kw, sym};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, InnerSpan, Span, Symbol, kw};
|
||||
use rustc_target::asm::InlineAsmArch;
|
||||
use smallvec::smallvec;
|
||||
use {rustc_ast as ast, rustc_parse_format as parse};
|
||||
|
|
@ -38,16 +38,16 @@ pub struct AsmArgs {
|
|||
/// - `Err(_)` if the current token matches the keyword, but was not expected
|
||||
fn eat_operand_keyword<'a>(
|
||||
p: &mut Parser<'a>,
|
||||
symbol: Symbol,
|
||||
exp: ExpKeywordPair,
|
||||
asm_macro: AsmMacro,
|
||||
) -> PResult<'a, bool> {
|
||||
if matches!(asm_macro, AsmMacro::Asm) {
|
||||
Ok(p.eat_keyword(symbol))
|
||||
Ok(p.eat_keyword(exp))
|
||||
} else {
|
||||
let span = p.token.span;
|
||||
if p.eat_keyword_noexpect(symbol) {
|
||||
if p.eat_keyword_noexpect(exp.kw) {
|
||||
// in gets printed as `r#in` otherwise
|
||||
let symbol = if symbol == kw::In { "in" } else { symbol.as_str() };
|
||||
let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() };
|
||||
Err(p.dcx().create_err(errors::AsmUnsupportedOperand {
|
||||
span,
|
||||
symbol,
|
||||
|
|
@ -95,13 +95,13 @@ pub fn parse_asm_args<'a>(
|
|||
|
||||
let mut allow_templates = true;
|
||||
while p.token != token::Eof {
|
||||
if !p.eat(&token::Comma) {
|
||||
if !p.eat(exp!(Comma)) {
|
||||
if allow_templates {
|
||||
// After a template string, we always expect *only* a comma...
|
||||
return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
|
||||
} else {
|
||||
// ...after that delegate to `expect` to also include the other expected tokens.
|
||||
return Err(p.expect(&token::Comma).err().unwrap());
|
||||
return Err(p.expect(exp!(Comma)).err().unwrap());
|
||||
}
|
||||
}
|
||||
if p.token == token::Eof {
|
||||
|
|
@ -109,14 +109,14 @@ pub fn parse_asm_args<'a>(
|
|||
} // accept trailing commas
|
||||
|
||||
// Parse clobber_abi
|
||||
if p.eat_keyword(sym::clobber_abi) {
|
||||
if p.eat_keyword(exp!(ClobberAbi)) {
|
||||
parse_clobber_abi(p, &mut args)?;
|
||||
allow_templates = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse options
|
||||
if p.eat_keyword(sym::options) {
|
||||
if p.eat_keyword(exp!(Options)) {
|
||||
parse_options(p, &mut args, asm_macro)?;
|
||||
allow_templates = false;
|
||||
continue;
|
||||
|
|
@ -128,7 +128,7 @@ pub fn parse_asm_args<'a>(
|
|||
let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
|
||||
let (ident, _) = p.token.ident().unwrap();
|
||||
p.bump();
|
||||
p.expect(&token::Eq)?;
|
||||
p.expect(exp!(Eq))?;
|
||||
allow_templates = false;
|
||||
Some(ident.name)
|
||||
} else {
|
||||
|
|
@ -136,57 +136,57 @@ pub fn parse_asm_args<'a>(
|
|||
};
|
||||
|
||||
let mut explicit_reg = false;
|
||||
let op = if eat_operand_keyword(p, kw::In, asm_macro)? {
|
||||
let op = if eat_operand_keyword(p, exp!(In), asm_macro)? {
|
||||
let reg = parse_reg(p, &mut explicit_reg)?;
|
||||
if p.eat_keyword(kw::Underscore) {
|
||||
if p.eat_keyword(exp!(Underscore)) {
|
||||
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
return Err(err);
|
||||
}
|
||||
let expr = p.parse_expr()?;
|
||||
ast::InlineAsmOperand::In { reg, expr }
|
||||
} else if eat_operand_keyword(p, sym::out, asm_macro)? {
|
||||
} else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
|
||||
let reg = parse_reg(p, &mut explicit_reg)?;
|
||||
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
|
||||
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
||||
ast::InlineAsmOperand::Out { reg, expr, late: false }
|
||||
} else if eat_operand_keyword(p, sym::lateout, asm_macro)? {
|
||||
} else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
|
||||
let reg = parse_reg(p, &mut explicit_reg)?;
|
||||
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
|
||||
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
||||
ast::InlineAsmOperand::Out { reg, expr, late: true }
|
||||
} else if eat_operand_keyword(p, sym::inout, asm_macro)? {
|
||||
} else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
|
||||
let reg = parse_reg(p, &mut explicit_reg)?;
|
||||
if p.eat_keyword(kw::Underscore) {
|
||||
if p.eat_keyword(exp!(Underscore)) {
|
||||
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
return Err(err);
|
||||
}
|
||||
let expr = p.parse_expr()?;
|
||||
if p.eat(&token::FatArrow) {
|
||||
if p.eat(exp!(FatArrow)) {
|
||||
let out_expr =
|
||||
if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
|
||||
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
||||
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
|
||||
} else {
|
||||
ast::InlineAsmOperand::InOut { reg, expr, late: false }
|
||||
}
|
||||
} else if eat_operand_keyword(p, sym::inlateout, asm_macro)? {
|
||||
} else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
|
||||
let reg = parse_reg(p, &mut explicit_reg)?;
|
||||
if p.eat_keyword(kw::Underscore) {
|
||||
if p.eat_keyword(exp!(Underscore)) {
|
||||
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
return Err(err);
|
||||
}
|
||||
let expr = p.parse_expr()?;
|
||||
if p.eat(&token::FatArrow) {
|
||||
if p.eat(exp!(FatArrow)) {
|
||||
let out_expr =
|
||||
if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
|
||||
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
||||
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
|
||||
} else {
|
||||
ast::InlineAsmOperand::InOut { reg, expr, late: true }
|
||||
}
|
||||
} else if eat_operand_keyword(p, sym::label, asm_macro)? {
|
||||
} else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
|
||||
let block = p.parse_block()?;
|
||||
ast::InlineAsmOperand::Label { block }
|
||||
} else if p.eat_keyword(kw::Const) {
|
||||
} else if p.eat_keyword(exp!(Const)) {
|
||||
let anon_const = p.parse_expr_anon_const()?;
|
||||
ast::InlineAsmOperand::Const { anon_const }
|
||||
} else if p.eat_keyword(sym::sym) {
|
||||
} else if p.eat_keyword(exp!(Sym)) {
|
||||
let expr = p.parse_expr()?;
|
||||
let ast::ExprKind::Path(qself, path) = &expr.kind else {
|
||||
let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
|
||||
|
|
@ -389,31 +389,31 @@ fn parse_options<'a>(
|
|||
) -> PResult<'a, ()> {
|
||||
let span_start = p.prev_token.span;
|
||||
|
||||
p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
|
||||
p.expect(exp!(OpenParen))?;
|
||||
|
||||
while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
|
||||
const OPTIONS: [(Symbol, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
|
||||
(sym::pure, ast::InlineAsmOptions::PURE),
|
||||
(sym::nomem, ast::InlineAsmOptions::NOMEM),
|
||||
(sym::readonly, ast::InlineAsmOptions::READONLY),
|
||||
(sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS),
|
||||
(sym::noreturn, ast::InlineAsmOptions::NORETURN),
|
||||
(sym::nostack, ast::InlineAsmOptions::NOSTACK),
|
||||
(sym::may_unwind, ast::InlineAsmOptions::MAY_UNWIND),
|
||||
(sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX),
|
||||
(kw::Raw, ast::InlineAsmOptions::RAW),
|
||||
while !p.eat(exp!(CloseParen)) {
|
||||
const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
|
||||
(exp!(Pure), ast::InlineAsmOptions::PURE),
|
||||
(exp!(Nomem), ast::InlineAsmOptions::NOMEM),
|
||||
(exp!(Readonly), ast::InlineAsmOptions::READONLY),
|
||||
(exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS),
|
||||
(exp!(Noreturn), ast::InlineAsmOptions::NORETURN),
|
||||
(exp!(Nostack), ast::InlineAsmOptions::NOSTACK),
|
||||
(exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND),
|
||||
(exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX),
|
||||
(exp!(Raw), ast::InlineAsmOptions::RAW),
|
||||
];
|
||||
|
||||
'blk: {
|
||||
for (symbol, option) in OPTIONS {
|
||||
for (exp, option) in OPTIONS {
|
||||
let kw_matched = if asm_macro.is_supported_option(option) {
|
||||
p.eat_keyword(symbol)
|
||||
p.eat_keyword(exp)
|
||||
} else {
|
||||
p.eat_keyword_noexpect(symbol)
|
||||
p.eat_keyword_noexpect(exp.kw)
|
||||
};
|
||||
|
||||
if kw_matched {
|
||||
try_set_option(p, args, asm_macro, symbol, option);
|
||||
try_set_option(p, args, asm_macro, exp.kw, option);
|
||||
break 'blk;
|
||||
}
|
||||
}
|
||||
|
|
@ -422,10 +422,10 @@ fn parse_options<'a>(
|
|||
}
|
||||
|
||||
// Allow trailing commas
|
||||
if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
|
||||
if p.eat(exp!(CloseParen)) {
|
||||
break;
|
||||
}
|
||||
p.expect(&token::Comma)?;
|
||||
p.expect(exp!(Comma))?;
|
||||
}
|
||||
|
||||
let new_span = span_start.to(p.prev_token.span);
|
||||
|
|
@ -437,14 +437,14 @@ fn parse_options<'a>(
|
|||
fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> {
|
||||
let span_start = p.prev_token.span;
|
||||
|
||||
p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
|
||||
p.expect(exp!(OpenParen))?;
|
||||
|
||||
if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
|
||||
if p.eat(exp!(CloseParen)) {
|
||||
return Err(p.dcx().create_err(errors::NonABI { span: p.token.span }));
|
||||
}
|
||||
|
||||
let mut new_abis = Vec::new();
|
||||
while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
|
||||
while !p.eat(exp!(CloseParen)) {
|
||||
match p.parse_str_lit() {
|
||||
Ok(str_lit) => {
|
||||
new_abis.push((str_lit.symbol_unescaped, str_lit.span));
|
||||
|
|
@ -456,10 +456,10 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
|
|||
};
|
||||
|
||||
// Allow trailing commas
|
||||
if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
|
||||
if p.eat(exp!(CloseParen)) {
|
||||
break;
|
||||
}
|
||||
p.expect(&token::Comma)?;
|
||||
p.expect(exp!(Comma))?;
|
||||
}
|
||||
|
||||
let full_span = span_start.to(p.prev_token.span);
|
||||
|
|
@ -482,7 +482,7 @@ fn parse_reg<'a>(
|
|||
p: &mut Parser<'a>,
|
||||
explicit_reg: &mut bool,
|
||||
) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
|
||||
p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
|
||||
p.expect(exp!(OpenParen))?;
|
||||
let result = match p.token.uninterpolate().kind {
|
||||
token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name),
|
||||
token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
|
||||
|
|
@ -496,7 +496,7 @@ fn parse_reg<'a>(
|
|||
}
|
||||
};
|
||||
p.bump();
|
||||
p.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
|
||||
p.expect(exp!(CloseParen))?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, tok
|
|||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
|
||||
use rustc_parse::exp;
|
||||
use rustc_parse::parser::Parser;
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
|
||||
use thin_vec::thin_vec;
|
||||
|
|
@ -143,7 +144,7 @@ fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<
|
|||
cx.dcx().emit_err(errors::AssertMissingComma { span: parser.token.span, comma });
|
||||
|
||||
parse_custom_message(&mut parser)
|
||||
} else if parser.eat(&token::Comma) {
|
||||
} else if parser.eat(exp!(Comma)) {
|
||||
parse_custom_message(&mut parser)
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use rustc_ast::token;
|
|||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
|
||||
use rustc_parse::exp;
|
||||
use rustc_span::Span;
|
||||
use {rustc_ast as ast, rustc_attr_parsing as attr};
|
||||
|
||||
|
|
@ -48,9 +49,9 @@ fn parse_cfg<'a>(
|
|||
|
||||
let cfg = p.parse_meta_item_inner()?;
|
||||
|
||||
let _ = p.eat(&token::Comma);
|
||||
let _ = p.eat(exp!(Comma));
|
||||
|
||||
if !p.eat(&token::Eof) {
|
||||
if !p.eat(exp!(Eof)) {
|
||||
return Err(cx.dcx().create_err(errors::OneCfgPattern { span }));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub(crate) fn expand_concat_idents<'cx>(
|
|||
}
|
||||
|
||||
let mut res_str = String::new();
|
||||
for (i, e) in tts.trees().enumerate() {
|
||||
for (i, e) in tts.iter().enumerate() {
|
||||
if i & 1 == 1 {
|
||||
match e {
|
||||
TokenTree::Token(Token { kind: token::Comma, .. }, _) => {}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use rustc_errors::{Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans
|
|||
use rustc_expand::base::*;
|
||||
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
|
||||
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId};
|
||||
use rustc_parse::exp;
|
||||
use rustc_parse_format as parse;
|
||||
use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol};
|
||||
|
||||
|
|
@ -93,12 +94,12 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a,
|
|||
let mut first = true;
|
||||
|
||||
while p.token != token::Eof {
|
||||
if !p.eat(&token::Comma) {
|
||||
if !p.eat(exp!(Comma)) {
|
||||
if first {
|
||||
p.clear_expected_tokens();
|
||||
p.clear_expected_token_types();
|
||||
}
|
||||
|
||||
match p.expect(&token::Comma) {
|
||||
match p.expect(exp!(Comma)) {
|
||||
Err(err) => {
|
||||
match token::TokenKind::Comma.similar_tokens() {
|
||||
Some(tks) if tks.contains(&p.token.kind) => {
|
||||
|
|
@ -122,7 +123,7 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a,
|
|||
match p.token.ident() {
|
||||
Some((ident, _)) if p.look_ahead(1, |t| *t == token::Eq) => {
|
||||
p.bump();
|
||||
p.expect(&token::Eq)?;
|
||||
p.expect(exp!(Eq))?;
|
||||
let expr = p.parse_expr()?;
|
||||
if let Some((_, prev)) = args.by_name(ident.name) {
|
||||
ecx.dcx().emit_err(errors::FormatDuplicateArg {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ use rustc_ast::tokenstream::TokenStream;
|
|||
use rustc_ast::{Pat, Ty, ast};
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_parse::exp;
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(crate) fn expand<'cx>(
|
||||
cx: &'cx mut ExtCtxt<'_>,
|
||||
|
|
@ -24,7 +25,7 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P
|
|||
let mut parser = cx.new_parser_from_tts(stream);
|
||||
|
||||
let ty = parser.parse_ty()?;
|
||||
parser.expect_keyword(sym::is)?;
|
||||
parser.expect_keyword(exp!(Is))?;
|
||||
let pat = parser.parse_pat_no_top_alt(None, None)?;
|
||||
|
||||
Ok((ty, pat))
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ pub(crate) fn expand_trace_macros(
|
|||
sp: Span,
|
||||
tt: TokenStream,
|
||||
) -> MacroExpanderResult<'static> {
|
||||
let mut cursor = tt.trees();
|
||||
let mut iter = tt.iter();
|
||||
let mut err = false;
|
||||
let value = match &cursor.next() {
|
||||
let value = match iter.next() {
|
||||
Some(TokenTree::Token(token, _)) if token.is_keyword(kw::True) => true,
|
||||
Some(TokenTree::Token(token, _)) if token.is_keyword(kw::False) => false,
|
||||
_ => {
|
||||
|
|
@ -19,7 +19,7 @@ pub(crate) fn expand_trace_macros(
|
|||
false
|
||||
}
|
||||
};
|
||||
err |= cursor.next().is_some();
|
||||
err |= iter.next().is_some();
|
||||
if err {
|
||||
cx.dcx().emit_err(errors::TraceMacros { span: sp });
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ 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::{parser, validate_attr};
|
||||
use rustc_parse::{exp, parser, validate_attr};
|
||||
use rustc_session::errors::report_lit_error;
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
|
||||
|
|
@ -204,7 +204,7 @@ pub(crate) fn get_single_expr_from_tts(
|
|||
Ok(ret) => ret,
|
||||
Err(guar) => return ExpandResult::Ready(Err(guar)),
|
||||
};
|
||||
let _ = p.eat(&token::Comma);
|
||||
let _ = p.eat(exp!(Comma));
|
||||
|
||||
if p.token != token::Eof {
|
||||
cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
|
||||
|
|
@ -237,7 +237,7 @@ pub(crate) fn get_exprs_from_tts(
|
|||
let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
|
||||
|
||||
es.push(expr);
|
||||
if p.eat(&token::Comma) {
|
||||
if p.eat(exp!(Comma)) {
|
||||
continue;
|
||||
}
|
||||
if p.token != token::Eof {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::ffi::c_int;
|
|||
#[cfg(feature = "jit")]
|
||||
use std::ffi::c_void;
|
||||
|
||||
// FIXME replace with core::ffi::c_size_t once stablized
|
||||
// FIXME replace with core::ffi::c_size_t once stabilized
|
||||
#[allow(non_camel_case_types)]
|
||||
#[cfg(feature = "jit")]
|
||||
type size_t = usize;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>(
|
|||
return;
|
||||
}
|
||||
match layout.variants {
|
||||
Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||
Variants::Single { index } => {
|
||||
assert_eq!(index, variant_index);
|
||||
}
|
||||
|
|
@ -85,6 +86,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>(
|
|||
}
|
||||
|
||||
let (tag_scalar, tag_field, tag_encoding) = match &layout.variants {
|
||||
Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||
Variants::Single { index } => {
|
||||
let discr_val = layout
|
||||
.ty
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
|
|||
sym::log2f64 => "log2",
|
||||
sym::fmaf32 => "fmaf",
|
||||
sym::fmaf64 => "fma",
|
||||
// FIXME: calling `fma` from libc without FMA target feature uses expensive sofware emulation
|
||||
// FIXME: calling `fma` from libc without FMA target feature uses expensive software emulation
|
||||
sym::fmuladdf32 => "fmaf", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f32
|
||||
sym::fmuladdf64 => "fma", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f64
|
||||
sym::fabsf32 => "fabsf",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion};
|
||||
|
||||
use crate::coverageinfo::mapgen::LocalFileId;
|
||||
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId};
|
||||
|
||||
/// Must match the layout of `LLVMRustCounterKind`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -126,30 +124,16 @@ pub(crate) struct CoverageSpan {
|
|||
/// Local index into the function's local-to-global file ID table.
|
||||
/// The value at that index is itself an index into the coverage filename
|
||||
/// table in the CGU's `__llvm_covmap` section.
|
||||
file_id: u32,
|
||||
pub(crate) file_id: u32,
|
||||
|
||||
/// 1-based starting line of the source code span.
|
||||
start_line: u32,
|
||||
pub(crate) start_line: u32,
|
||||
/// 1-based starting column of the source code span.
|
||||
start_col: u32,
|
||||
pub(crate) start_col: u32,
|
||||
/// 1-based ending line of the source code span.
|
||||
end_line: u32,
|
||||
pub(crate) end_line: u32,
|
||||
/// 1-based ending column of the source code span. High bit must be unset.
|
||||
end_col: u32,
|
||||
}
|
||||
|
||||
impl CoverageSpan {
|
||||
pub(crate) fn from_source_region(
|
||||
local_file_id: LocalFileId,
|
||||
code_region: &SourceRegion,
|
||||
) -> Self {
|
||||
let file_id = local_file_id.as_u32();
|
||||
let &SourceRegion { start_line, start_col, end_line, end_col } = code_region;
|
||||
// Internally, LLVM uses the high bit of `end_col` to distinguish between
|
||||
// code regions and gap regions, so it can't be used by the column number.
|
||||
assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}");
|
||||
Self { file_id, start_line, start_col, end_line, end_col }
|
||||
}
|
||||
pub(crate) end_col: u32,
|
||||
}
|
||||
|
||||
/// Holds tables of the various region types in one struct.
|
||||
|
|
@ -184,7 +168,7 @@ impl Regions {
|
|||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct CodeRegion {
|
||||
pub(crate) span: CoverageSpan,
|
||||
pub(crate) cov_span: CoverageSpan,
|
||||
pub(crate) counter: Counter,
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +176,7 @@ pub(crate) struct CodeRegion {
|
|||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct BranchRegion {
|
||||
pub(crate) span: CoverageSpan,
|
||||
pub(crate) cov_span: CoverageSpan,
|
||||
pub(crate) true_counter: Counter,
|
||||
pub(crate) false_counter: Counter,
|
||||
}
|
||||
|
|
@ -201,7 +185,7 @@ pub(crate) struct BranchRegion {
|
|||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct MCDCBranchRegion {
|
||||
pub(crate) span: CoverageSpan,
|
||||
pub(crate) cov_span: CoverageSpan,
|
||||
pub(crate) true_counter: Counter,
|
||||
pub(crate) false_counter: Counter,
|
||||
pub(crate) mcdc_branch_params: mcdc::BranchParameters,
|
||||
|
|
@ -211,6 +195,6 @@ pub(crate) struct MCDCBranchRegion {
|
|||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct MCDCDecisionRegion {
|
||||
pub(crate) span: CoverageSpan,
|
||||
pub(crate) cov_span: CoverageSpan,
|
||||
pub(crate) mcdc_decision_params: mcdc::DecisionParameters,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,11 +40,10 @@ pub(crate) fn create_pgo_func_name_var<'ll>(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn write_filenames_to_buffer<'a>(
|
||||
filenames: impl IntoIterator<Item = &'a str>,
|
||||
) -> Vec<u8> {
|
||||
pub(crate) fn write_filenames_to_buffer(filenames: &[impl AsRef<str>]) -> Vec<u8> {
|
||||
let (pointers, lengths) = filenames
|
||||
.into_iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(|s: &str| (s.as_c_char_ptr(), s.len()))
|
||||
.unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_abi::Align;
|
||||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
|
||||
};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir;
|
||||
|
|
@ -13,7 +13,7 @@ use rustc_middle::ty::{self, TyCtxt};
|
|||
use rustc_session::RemapFileNameExt;
|
||||
use rustc_session::config::RemapPathScopeComponents;
|
||||
use rustc_span::def_id::DefIdSet;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_span::{SourceFile, StableSourceFileId};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::common::CodegenCx;
|
||||
|
|
@ -22,6 +22,7 @@ use crate::coverageinfo::mapgen::covfun::prepare_covfun_record;
|
|||
use crate::llvm;
|
||||
|
||||
mod covfun;
|
||||
mod spans;
|
||||
|
||||
/// Generates and exports the coverage map, which is embedded in special
|
||||
/// linker sections in the final binary.
|
||||
|
|
@ -131,45 +132,51 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
generate_covmap_record(cx, covmap_version, &filenames_buffer);
|
||||
}
|
||||
|
||||
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
|
||||
/// Maps "global" (per-CGU) file ID numbers to their underlying source files.
|
||||
struct GlobalFileTable {
|
||||
/// This "raw" table doesn't include the working dir, so a filename's
|
||||
/// This "raw" table doesn't include the working dir, so a file's
|
||||
/// global ID is its index in this set **plus one**.
|
||||
raw_file_table: FxIndexSet<Symbol>,
|
||||
raw_file_table: FxIndexMap<StableSourceFileId, Arc<SourceFile>>,
|
||||
}
|
||||
|
||||
impl GlobalFileTable {
|
||||
fn new() -> Self {
|
||||
Self { raw_file_table: FxIndexSet::default() }
|
||||
Self { raw_file_table: FxIndexMap::default() }
|
||||
}
|
||||
|
||||
fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> GlobalFileId {
|
||||
fn global_file_id_for_file(&mut self, file: &Arc<SourceFile>) -> GlobalFileId {
|
||||
// Ensure the given file has a table entry, and get its index.
|
||||
let (raw_id, _) = self.raw_file_table.insert_full(file_name);
|
||||
let entry = self.raw_file_table.entry(file.stable_id);
|
||||
let raw_id = entry.index();
|
||||
entry.or_insert_with(|| Arc::clone(file));
|
||||
|
||||
// The raw file table doesn't include an entry for the working dir
|
||||
// (which has ID 0), so add 1 to get the correct ID.
|
||||
GlobalFileId::from_usize(raw_id + 1)
|
||||
}
|
||||
|
||||
fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
|
||||
let mut table = Vec::with_capacity(self.raw_file_table.len() + 1);
|
||||
|
||||
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
|
||||
// requires setting the first filename to the compilation directory.
|
||||
// Since rustc generates coverage maps with relative paths, the
|
||||
// compilation directory can be combined with the relative paths
|
||||
// to get absolute paths, if needed.
|
||||
use rustc_session::RemapFileNameExt;
|
||||
use rustc_session::config::RemapPathScopeComponents;
|
||||
let working_dir: &str = &tcx
|
||||
.sess
|
||||
.opts
|
||||
.working_dir
|
||||
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
|
||||
.to_string_lossy();
|
||||
table.push(
|
||||
tcx.sess
|
||||
.opts
|
||||
.working_dir
|
||||
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
|
||||
.to_string_lossy(),
|
||||
);
|
||||
|
||||
// Insert the working dir at index 0, before the other filenames.
|
||||
let filenames =
|
||||
iter::once(working_dir).chain(self.raw_file_table.iter().map(Symbol::as_str));
|
||||
llvm_cov::write_filenames_to_buffer(filenames)
|
||||
// Add the regular entries after the base directory.
|
||||
table.extend(self.raw_file_table.values().map(|file| {
|
||||
file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy()
|
||||
}));
|
||||
|
||||
llvm_cov::write_filenames_to_buffer(&table)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +189,7 @@ rustc_index::newtype_index! {
|
|||
/// An index into a function's list of global file IDs. That underlying list
|
||||
/// of local-to-global mappings will be embedded in the function's record in
|
||||
/// the `__llvm_covfun` linker section.
|
||||
pub(crate) struct LocalFileId {}
|
||||
struct LocalFileId {}
|
||||
}
|
||||
|
||||
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
|
||||
|
|
@ -208,13 +215,6 @@ impl VirtualFileMapping {
|
|||
}
|
||||
}
|
||||
|
||||
fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
|
||||
let source_file = tcx.sess.source_map().lookup_source_file(span.lo());
|
||||
let name =
|
||||
source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy();
|
||||
Symbol::intern(&name)
|
||||
}
|
||||
|
||||
/// Generates the contents of the covmap record for this CGU, which mostly
|
||||
/// consists of a header and a list of filenames. The record is then stored
|
||||
/// as a global variable in the `__llvm_covmap` section.
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@ use rustc_abi::Align;
|
|||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
|
||||
};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::coverage::{
|
||||
CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op,
|
||||
};
|
||||
use rustc_middle::ty::{Instance, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::HasTargetSpec;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, span_file_name};
|
||||
use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, spans};
|
||||
use crate::coverageinfo::{ffi, llvm_cov};
|
||||
use crate::llvm;
|
||||
|
||||
|
|
@ -67,12 +67,8 @@ pub(crate) fn prepare_covfun_record<'tcx>(
|
|||
fill_region_tables(tcx, global_file_table, fn_cov_info, ids_info, &mut covfun);
|
||||
|
||||
if covfun.regions.has_no_regions() {
|
||||
if covfun.is_used {
|
||||
bug!("a used function should have had coverage mapping data but did not: {covfun:?}");
|
||||
} else {
|
||||
debug!(?covfun, "unused function had no coverage mapping data");
|
||||
return None;
|
||||
}
|
||||
debug!(?covfun, "function has no mappings to embed; skipping");
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(covfun)
|
||||
|
|
@ -121,41 +117,58 @@ fn fill_region_tables<'tcx>(
|
|||
covfun: &mut CovfunRecord<'tcx>,
|
||||
) {
|
||||
// Currently a function's mappings must all be in the same file as its body span.
|
||||
let file_name = span_file_name(tcx, fn_cov_info.body_span);
|
||||
let source_map = tcx.sess.source_map();
|
||||
let source_file = source_map.lookup_source_file(fn_cov_info.body_span.lo());
|
||||
|
||||
// Look up the global file ID for that filename.
|
||||
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
|
||||
// Look up the global file ID for that file.
|
||||
let global_file_id = global_file_table.global_file_id_for_file(&source_file);
|
||||
|
||||
// Associate that global file ID with a local file ID for this function.
|
||||
let local_file_id = covfun.virtual_file_mapping.local_id_for_global(global_file_id);
|
||||
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");
|
||||
|
||||
let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } =
|
||||
&mut covfun.regions;
|
||||
|
||||
let make_cov_span = |span: Span| {
|
||||
spans::make_coverage_span(local_file_id, source_map, fn_cov_info, &source_file, span)
|
||||
};
|
||||
let discard_all = tcx.sess.coverage_discard_all_spans_in_codegen();
|
||||
|
||||
// For each counter/region pair in this function+file, convert it to a
|
||||
// form suitable for FFI.
|
||||
let is_zero_term = |term| !covfun.is_used || ids_info.is_zero_term(term);
|
||||
for Mapping { kind, ref source_region } in &fn_cov_info.mappings {
|
||||
for &Mapping { ref kind, span } in &fn_cov_info.mappings {
|
||||
// If the mapping refers to counters/expressions that were removed by
|
||||
// MIR opts, replace those occurrences with zero.
|
||||
let kind = kind.map_terms(|term| if is_zero_term(term) { CovTerm::Zero } else { term });
|
||||
|
||||
let span = ffi::CoverageSpan::from_source_region(local_file_id, source_region);
|
||||
// Convert the `Span` into coordinates that we can pass to LLVM, or
|
||||
// discard the span if conversion fails. In rare, cases _all_ of a
|
||||
// function's spans are discarded, and the rest of coverage codegen
|
||||
// needs to handle that gracefully to avoid a repeat of #133606.
|
||||
// We don't have a good test case for triggering that organically, so
|
||||
// instead we set `-Zcoverage-options=discard-all-spans-in-codegen`
|
||||
// to force it to occur.
|
||||
let Some(cov_span) = make_cov_span(span) else { continue };
|
||||
if discard_all {
|
||||
continue;
|
||||
}
|
||||
|
||||
match kind {
|
||||
MappingKind::Code(term) => {
|
||||
code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
|
||||
code_regions
|
||||
.push(ffi::CodeRegion { cov_span, counter: ffi::Counter::from_term(term) });
|
||||
}
|
||||
MappingKind::Branch { true_term, false_term } => {
|
||||
branch_regions.push(ffi::BranchRegion {
|
||||
span,
|
||||
cov_span,
|
||||
true_counter: ffi::Counter::from_term(true_term),
|
||||
false_counter: ffi::Counter::from_term(false_term),
|
||||
});
|
||||
}
|
||||
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
|
||||
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
|
||||
span,
|
||||
cov_span,
|
||||
true_counter: ffi::Counter::from_term(true_term),
|
||||
false_counter: ffi::Counter::from_term(false_term),
|
||||
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
|
||||
|
|
@ -163,7 +176,7 @@ fn fill_region_tables<'tcx>(
|
|||
}
|
||||
MappingKind::MCDCDecision(mcdc_decision_params) => {
|
||||
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
|
||||
span,
|
||||
cov_span,
|
||||
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
126
compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs
Normal file
126
compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use rustc_middle::mir::coverage::FunctionCoverageInfo;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, Pos, SourceFile, Span};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::coverageinfo::ffi;
|
||||
use crate::coverageinfo::mapgen::LocalFileId;
|
||||
|
||||
/// Converts the span into its start line and column, and end line and column.
|
||||
///
|
||||
/// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by
|
||||
/// the compiler, these column numbers are denoted in **bytes**, because that's what
|
||||
/// LLVM's `llvm-cov` tool expects to see in coverage maps.
|
||||
///
|
||||
/// Returns `None` if the conversion failed for some reason. This shouldn't happen,
|
||||
/// but it's hard to rule out entirely (especially in the presence of complex macros
|
||||
/// or other expansions), and if it does happen then skipping a span or function is
|
||||
/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid.
|
||||
pub(crate) fn make_coverage_span(
|
||||
file_id: LocalFileId,
|
||||
source_map: &SourceMap,
|
||||
fn_cov_info: &FunctionCoverageInfo,
|
||||
file: &SourceFile,
|
||||
span: Span,
|
||||
) -> Option<ffi::CoverageSpan> {
|
||||
let span = ensure_non_empty_span(source_map, fn_cov_info, span)?;
|
||||
|
||||
let lo = span.lo();
|
||||
let hi = span.hi();
|
||||
|
||||
// Column numbers need to be in bytes, so we can't use the more convenient
|
||||
// `SourceMap` methods for looking up file coordinates.
|
||||
let line_and_byte_column = |pos: BytePos| -> Option<(usize, usize)> {
|
||||
let rpos = file.relative_position(pos);
|
||||
let line_index = file.lookup_line(rpos)?;
|
||||
let line_start = file.lines()[line_index];
|
||||
// Line numbers and column numbers are 1-based, so add 1 to each.
|
||||
Some((line_index + 1, (rpos - line_start).to_usize() + 1))
|
||||
};
|
||||
|
||||
let (mut start_line, start_col) = line_and_byte_column(lo)?;
|
||||
let (mut end_line, end_col) = line_and_byte_column(hi)?;
|
||||
|
||||
// Apply an offset so that code in doctests has correct line numbers.
|
||||
// FIXME(#79417): Currently we have no way to offset doctest _columns_.
|
||||
start_line = source_map.doctest_offset_line(&file.name, start_line);
|
||||
end_line = source_map.doctest_offset_line(&file.name, end_line);
|
||||
|
||||
check_coverage_span(ffi::CoverageSpan {
|
||||
file_id: file_id.as_u32(),
|
||||
start_line: start_line as u32,
|
||||
start_col: start_col as u32,
|
||||
end_line: end_line as u32,
|
||||
end_col: end_col as u32,
|
||||
})
|
||||
}
|
||||
|
||||
fn ensure_non_empty_span(
|
||||
source_map: &SourceMap,
|
||||
fn_cov_info: &FunctionCoverageInfo,
|
||||
span: Span,
|
||||
) -> Option<Span> {
|
||||
if !span.is_empty() {
|
||||
return Some(span);
|
||||
}
|
||||
|
||||
let lo = span.lo();
|
||||
let hi = span.hi();
|
||||
|
||||
// The span is empty, so try to expand it to cover an adjacent '{' or '}',
|
||||
// but only within the bounds of the body span.
|
||||
let try_next = hi < fn_cov_info.body_span.hi();
|
||||
let try_prev = fn_cov_info.body_span.lo() < lo;
|
||||
if !(try_next || try_prev) {
|
||||
return None;
|
||||
}
|
||||
|
||||
source_map
|
||||
.span_to_source(span, |src, start, end| try {
|
||||
// Adjusting span endpoints by `BytePos(1)` is normally a bug,
|
||||
// but in this case we have specifically checked that the character
|
||||
// we're skipping over is one of two specific ASCII characters, so
|
||||
// adjusting by exactly 1 byte is correct.
|
||||
if try_next && src.as_bytes()[end] == b'{' {
|
||||
Some(span.with_hi(hi + BytePos(1)))
|
||||
} else if try_prev && src.as_bytes()[start - 1] == b'}' {
|
||||
Some(span.with_lo(lo - BytePos(1)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok()?
|
||||
}
|
||||
|
||||
/// If `llvm-cov` sees a source region that is improperly ordered (end < start),
|
||||
/// it will immediately exit with a fatal error. To prevent that from happening,
|
||||
/// discard regions that are improperly ordered, or might be interpreted in a
|
||||
/// way that makes them improperly ordered.
|
||||
fn check_coverage_span(cov_span: ffi::CoverageSpan) -> Option<ffi::CoverageSpan> {
|
||||
let ffi::CoverageSpan { file_id: _, start_line, start_col, end_line, end_col } = cov_span;
|
||||
|
||||
// Line/column coordinates are supposed to be 1-based. If we ever emit
|
||||
// coordinates of 0, `llvm-cov` might misinterpret them.
|
||||
let all_nonzero = [start_line, start_col, end_line, end_col].into_iter().all(|x| x != 0);
|
||||
// Coverage mappings use the high bit of `end_col` to indicate that a
|
||||
// region is actually a "gap" region, so make sure it's unset.
|
||||
let end_col_has_high_bit_unset = (end_col & (1 << 31)) == 0;
|
||||
// If a region is improperly ordered (end < start), `llvm-cov` will exit
|
||||
// with a fatal error, which is inconvenient for users and hard to debug.
|
||||
let is_ordered = (start_line, start_col) <= (end_line, end_col);
|
||||
|
||||
if all_nonzero && end_col_has_high_bit_unset && is_ordered {
|
||||
Some(cov_span)
|
||||
} else {
|
||||
debug!(
|
||||
?cov_span,
|
||||
?all_nonzero,
|
||||
?end_col_has_high_bit_unset,
|
||||
?is_ordered,
|
||||
"Skipping source region that would be misinterpreted or rejected by LLVM"
|
||||
);
|
||||
// If this happens in a debug build, ICE to make it easier to notice.
|
||||
debug_assert!(false, "Improper source region: {cov_span:?}");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -212,21 +212,17 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
|
|||
),
|
||||
|cx, enum_type_di_node| {
|
||||
match enum_type_and_layout.variants {
|
||||
Variants::Single { index: variant_index } => {
|
||||
if enum_adt_def.variants().is_empty() {
|
||||
// Uninhabited enums have Variants::Single. We don't generate
|
||||
// any members for them.
|
||||
return smallvec![];
|
||||
}
|
||||
|
||||
build_single_variant_union_fields(
|
||||
cx,
|
||||
enum_adt_def,
|
||||
enum_type_and_layout,
|
||||
enum_type_di_node,
|
||||
variant_index,
|
||||
)
|
||||
Variants::Empty => {
|
||||
// We don't generate any members for uninhabited types.
|
||||
return smallvec![];
|
||||
}
|
||||
Variants::Single { index: variant_index } => build_single_variant_union_fields(
|
||||
cx,
|
||||
enum_adt_def,
|
||||
enum_type_and_layout,
|
||||
enum_type_di_node,
|
||||
variant_index,
|
||||
),
|
||||
Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
ref variants,
|
||||
|
|
@ -303,6 +299,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
|
|||
)
|
||||
}
|
||||
Variants::Single { .. }
|
||||
| Variants::Empty
|
||||
| Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => {
|
||||
bug!(
|
||||
"Encountered coroutine with non-direct-tag layout: {:?}",
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ fn compute_discriminant_value<'ll, 'tcx>(
|
|||
variant_index: VariantIdx,
|
||||
) -> DiscrResult {
|
||||
match enum_type_and_layout.layout.variants() {
|
||||
&Variants::Single { .. } => DiscrResult::NoDiscriminant,
|
||||
&Variants::Single { .. } | &Variants::Empty => DiscrResult::NoDiscriminant,
|
||||
&Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value(
|
||||
enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -358,8 +358,8 @@ fn build_discr_member_di_node<'ll, 'tcx>(
|
|||
let containing_scope = enum_or_coroutine_type_di_node;
|
||||
|
||||
match enum_or_coroutine_type_and_layout.layout.variants() {
|
||||
// A single-variant enum has no discriminant.
|
||||
&Variants::Single { .. } => None,
|
||||
// A single-variant or no-variant enum has no discriminant.
|
||||
&Variants::Single { .. } | &Variants::Empty => None,
|
||||
|
||||
&Variants::Multiple { tag_field, .. } => {
|
||||
let tag_base_type = tag_base_type(cx.tcx, enum_or_coroutine_type_and_layout);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#![feature(iter_intersperse)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(try_blocks)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ fn uncached_llvm_type<'a, 'tcx>(
|
|||
if let (&ty::Adt(def, _), &Variants::Single { index }) =
|
||||
(layout.ty.kind(), &layout.variants)
|
||||
{
|
||||
if def.is_enum() && !def.variants().is_empty() {
|
||||
if def.is_enum() {
|
||||
write!(&mut name, "::{}", def.variant(index).name).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ edition = "2021"
|
|||
ar_archive_writer = "0.4.2"
|
||||
arrayvec = { version = "0.7", default-features = false }
|
||||
bitflags = "2.4.1"
|
||||
cc = "1.1.23"
|
||||
# Pinned so `cargo update` bumps don't cause breakage
|
||||
cc = "=1.2.0"
|
||||
either = "1.5.0"
|
||||
itertools = "0.12"
|
||||
pathdiff = "0.2.0"
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ fn tag_base_type_opt<'tcx>(
|
|||
});
|
||||
|
||||
match enum_type_and_layout.layout.variants() {
|
||||
// A single-variant enum has no discriminant.
|
||||
Variants::Single { .. } => None,
|
||||
// A single-variant or no-variant enum has no discriminant.
|
||||
Variants::Single { .. } | Variants::Empty => None,
|
||||
|
||||
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
|
||||
// Niche tags are always normalized to unsized integers of the correct size.
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
|||
return bx.cx().const_poison(cast_to);
|
||||
}
|
||||
let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants {
|
||||
Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||
Variants::Single { index } => {
|
||||
let discr_val = self
|
||||
.layout
|
||||
|
|
@ -365,9 +366,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
|||
return;
|
||||
}
|
||||
match self.layout.variants {
|
||||
Variants::Single { index } => {
|
||||
assert_eq!(index, variant_index);
|
||||
}
|
||||
Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||
Variants::Single { index } => assert_eq!(index, variant_index),
|
||||
|
||||
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => {
|
||||
let ptr = self.project_field(bx, tag_field);
|
||||
let to =
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Read discriminant, return the runtime value as well as the variant index.
|
||||
/// Read discriminant, return the variant index.
|
||||
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
|
||||
///
|
||||
/// Will never return an uninhabited variant.
|
||||
|
|
@ -65,21 +65,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// We use "tag" to refer to how the discriminant is encoded in memory, which can be either
|
||||
// straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
|
||||
let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout().variants {
|
||||
Variants::Empty => {
|
||||
throw_ub!(UninhabitedEnumVariantRead(None));
|
||||
}
|
||||
Variants::Single { index } => {
|
||||
// Do some extra checks on enums.
|
||||
if ty.is_enum() {
|
||||
// Hilariously, `Single` is used even for 0-variant enums.
|
||||
// (See https://github.com/rust-lang/rust/issues/89765).
|
||||
if ty.ty_adt_def().unwrap().variants().is_empty() {
|
||||
throw_ub!(UninhabitedEnumVariantRead(index))
|
||||
}
|
||||
if op.layout().is_uninhabited() {
|
||||
// For consistency with `write_discriminant`, and to make sure that
|
||||
// `project_downcast` cannot fail due to strange layouts, we declare immediate UB
|
||||
// for uninhabited variants.
|
||||
if op.layout().for_variant(self, index).is_uninhabited() {
|
||||
throw_ub!(UninhabitedEnumVariantRead(index))
|
||||
}
|
||||
// for uninhabited enums.
|
||||
throw_ub!(UninhabitedEnumVariantRead(Some(index)));
|
||||
}
|
||||
// Since the type is inhabited, there must be an index.
|
||||
return interp_ok(index);
|
||||
}
|
||||
Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
|
||||
|
|
@ -199,11 +195,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// `uninhabited_enum_branching` MIR pass. It also ensures consistency with
|
||||
// `write_discriminant`.
|
||||
if op.layout().for_variant(self, index).is_uninhabited() {
|
||||
throw_ub!(UninhabitedEnumVariantRead(index))
|
||||
throw_ub!(UninhabitedEnumVariantRead(Some(index)))
|
||||
}
|
||||
interp_ok(index)
|
||||
}
|
||||
|
||||
/// Read discriminant, return the user-visible discriminant.
|
||||
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
|
||||
pub fn discriminant_for_variant(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
|
|
@ -243,6 +241,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
|
||||
match layout.variants {
|
||||
abi::Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||
abi::Variants::Single { .. } => {
|
||||
// The tag of a `Single` enum is like the tag of the niched
|
||||
// variant: there's no tag as the discriminant is encoded
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ where
|
|||
let actual_to = if from_end {
|
||||
if from.checked_add(to).is_none_or(|to| to > len) {
|
||||
// This can only be reached in ConstProp and non-rustc-MIR.
|
||||
throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) });
|
||||
throw_ub!(BoundsCheckFailed { len, index: from.saturating_add(to) });
|
||||
}
|
||||
len.checked_sub(to).unwrap()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
};
|
||||
}
|
||||
}
|
||||
Variants::Single { .. } => {}
|
||||
Variants::Single { .. } | Variants::Empty => {}
|
||||
}
|
||||
|
||||
// Now we know we are projecting to a field, so figure out which one.
|
||||
|
|
@ -344,6 +344,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
// Inside a variant
|
||||
PathElem::Field(def.variant(index).fields[FieldIdx::from_usize(field)].name)
|
||||
}
|
||||
Variants::Empty => panic!("there is no field in Variants::Empty types"),
|
||||
Variants::Multiple { .. } => bug!("we handled variants above"),
|
||||
}
|
||||
}
|
||||
|
|
@ -1010,7 +1011,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
}
|
||||
// Don't forget potential other variants.
|
||||
match &layout.variants {
|
||||
Variants::Single { .. } => {
|
||||
Variants::Single { .. } | Variants::Empty => {
|
||||
// Fully handled above.
|
||||
}
|
||||
Variants::Multiple { variants, .. } => {
|
||||
|
|
|
|||
|
|
@ -218,8 +218,8 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
|
|||
// recurse with the inner type
|
||||
self.visit_variant(v, idx, &inner)?;
|
||||
}
|
||||
// For single-variant layouts, we already did anything there is to do.
|
||||
Variants::Single { .. } => {}
|
||||
// For single-variant layouts, we already did everything there is to do.
|
||||
Variants::Single { .. } | Variants::Empty => {}
|
||||
}
|
||||
|
||||
interp_ok(())
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ fn check_validity_requirement_lax<'tcx>(
|
|||
}
|
||||
|
||||
match &this.variants {
|
||||
Variants::Empty => return Ok(false),
|
||||
Variants::Single { .. } => {
|
||||
// All fields of this single variant have already been checked above, there is nothing
|
||||
// else to do.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use std::fmt::Write as _;
|
|||
use std::fs::{self, File};
|
||||
use std::io::{self, IsTerminal, Read, Write};
|
||||
use std::panic::{self, PanicHookInfo, catch_unwind};
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{self, Command, Stdio};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
|
@ -460,7 +460,7 @@ fn run_compiler(
|
|||
})
|
||||
}
|
||||
|
||||
fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &PathBuf) {
|
||||
fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) {
|
||||
let output_filenames = tcxt.output_filenames(());
|
||||
let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-");
|
||||
let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json");
|
||||
|
|
|
|||
|
|
@ -2523,7 +2523,7 @@ impl HumanEmitter {
|
|||
buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
|
||||
}
|
||||
[] => {
|
||||
// FIXME: needed? Doesn't get excercised in any test.
|
||||
// FIXME: needed? Doesn't get exercised in any test.
|
||||
self.draw_col_separator_no_space(buffer, *row_num, max_line_num_len + 1);
|
||||
}
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ fn parse_with_end_pat<'a>(
|
|||
None
|
||||
}
|
||||
|
||||
/// Resturn `(match, residual)` to end of line. The EOL is returned with the
|
||||
/// Return `(match, residual)` to end of line. The EOL is returned with the
|
||||
/// residual.
|
||||
fn parse_to_newline(buf: &[u8]) -> (&[u8], &[u8]) {
|
||||
buf.iter().position(|ch| *ch == b'\n').map_or((buf, &[]), |pos| buf.split_at(pos))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
|
||||
use rustc_ast::tokenstream::{TokenStream, TokenStreamIter, TokenTree};
|
||||
use rustc_ast::{LitIntType, LitKind};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Applicability, PResult};
|
||||
|
|
@ -38,14 +38,14 @@ impl MetaVarExpr {
|
|||
outer_span: Span,
|
||||
psess: &'psess ParseSess,
|
||||
) -> PResult<'psess, MetaVarExpr> {
|
||||
let mut tts = input.trees();
|
||||
let ident = parse_ident(&mut tts, psess, outer_span)?;
|
||||
let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = tts.next() else {
|
||||
let mut iter = input.iter();
|
||||
let ident = parse_ident(&mut iter, psess, outer_span)?;
|
||||
let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = iter.next() else {
|
||||
let msg = "meta-variable expression parameter must be wrapped in parentheses";
|
||||
return Err(psess.dcx().struct_span_err(ident.span, msg));
|
||||
};
|
||||
check_trailing_token(&mut tts, psess)?;
|
||||
let mut iter = args.trees();
|
||||
check_trailing_token(&mut iter, psess)?;
|
||||
let mut iter = args.iter();
|
||||
let rslt = match ident.as_str() {
|
||||
"concat" => {
|
||||
let mut result = Vec::new();
|
||||
|
|
@ -73,7 +73,7 @@ impl MetaVarExpr {
|
|||
}
|
||||
};
|
||||
result.push(element);
|
||||
if iter.look_ahead(0).is_none() {
|
||||
if iter.peek().is_none() {
|
||||
break;
|
||||
}
|
||||
if !try_eat_comma(&mut iter) {
|
||||
|
|
@ -142,7 +142,7 @@ pub(crate) enum MetaVarExprConcatElem {
|
|||
|
||||
// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
|
||||
fn check_trailing_token<'psess>(
|
||||
iter: &mut RefTokenTreeCursor<'_>,
|
||||
iter: &mut TokenStreamIter<'_>,
|
||||
psess: &'psess ParseSess,
|
||||
) -> PResult<'psess, ()> {
|
||||
if let Some(tt) = iter.next() {
|
||||
|
|
@ -158,14 +158,14 @@ fn check_trailing_token<'psess>(
|
|||
|
||||
/// Parse a meta-variable `count` expression: `count(ident[, depth])`
|
||||
fn parse_count<'psess>(
|
||||
iter: &mut RefTokenTreeCursor<'_>,
|
||||
iter: &mut TokenStreamIter<'_>,
|
||||
psess: &'psess ParseSess,
|
||||
span: Span,
|
||||
) -> PResult<'psess, MetaVarExpr> {
|
||||
eat_dollar(iter, psess, span)?;
|
||||
let ident = parse_ident(iter, psess, span)?;
|
||||
let depth = if try_eat_comma(iter) {
|
||||
if iter.look_ahead(0).is_none() {
|
||||
if iter.peek().is_none() {
|
||||
return Err(psess.dcx().struct_span_err(
|
||||
span,
|
||||
"`count` followed by a comma must have an associated index indicating its depth",
|
||||
|
|
@ -180,7 +180,7 @@ fn parse_count<'psess>(
|
|||
|
||||
/// Parses the depth used by index(depth) and len(depth).
|
||||
fn parse_depth<'psess>(
|
||||
iter: &mut RefTokenTreeCursor<'_>,
|
||||
iter: &mut TokenStreamIter<'_>,
|
||||
psess: &'psess ParseSess,
|
||||
span: Span,
|
||||
) -> PResult<'psess, usize> {
|
||||
|
|
@ -203,7 +203,7 @@ fn parse_depth<'psess>(
|
|||
|
||||
/// Parses an generic ident
|
||||
fn parse_ident<'psess>(
|
||||
iter: &mut RefTokenTreeCursor<'_>,
|
||||
iter: &mut TokenStreamIter<'_>,
|
||||
psess: &'psess ParseSess,
|
||||
fallback_span: Span,
|
||||
) -> PResult<'psess, Ident> {
|
||||
|
|
@ -235,7 +235,7 @@ fn parse_ident_from_token<'psess>(
|
|||
}
|
||||
|
||||
fn parse_token<'psess, 't>(
|
||||
iter: &mut RefTokenTreeCursor<'t>,
|
||||
iter: &mut TokenStreamIter<'t>,
|
||||
psess: &'psess ParseSess,
|
||||
fallback_span: Span,
|
||||
) -> PResult<'psess, &'t Token> {
|
||||
|
|
@ -250,8 +250,8 @@ fn parse_token<'psess, 't>(
|
|||
|
||||
/// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
|
||||
/// iterator is not modified and the result is `false`.
|
||||
fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
|
||||
if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) {
|
||||
fn try_eat_comma(iter: &mut TokenStreamIter<'_>) -> bool {
|
||||
if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.peek() {
|
||||
let _ = iter.next();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -260,8 +260,8 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
|
|||
|
||||
/// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the
|
||||
/// iterator is not modified and the result is `false`.
|
||||
fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool {
|
||||
if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) {
|
||||
fn try_eat_dollar(iter: &mut TokenStreamIter<'_>) -> bool {
|
||||
if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.peek() {
|
||||
let _ = iter.next();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -270,12 +270,11 @@ fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool {
|
|||
|
||||
/// Expects that the next item is a dollar sign.
|
||||
fn eat_dollar<'psess>(
|
||||
iter: &mut RefTokenTreeCursor<'_>,
|
||||
iter: &mut TokenStreamIter<'_>,
|
||||
psess: &'psess ParseSess,
|
||||
span: Span,
|
||||
) -> PResult<'psess, ()> {
|
||||
if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) {
|
||||
let _ = iter.next();
|
||||
if try_eat_dollar(iter) {
|
||||
return Ok(());
|
||||
}
|
||||
Err(psess.dcx().struct_span_err(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, Token};
|
||||
use rustc_ast::tokenstream::TokenStreamIter;
|
||||
use rustc_ast::{NodeId, tokenstream};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_feature::Features;
|
||||
|
|
@ -48,25 +49,25 @@ pub(super) fn parse(
|
|||
|
||||
// For each token tree in `input`, parse the token into a `self::TokenTree`, consuming
|
||||
// additional trees if need be.
|
||||
let mut trees = input.trees().peekable();
|
||||
while let Some(tree) = trees.next() {
|
||||
let mut iter = input.iter();
|
||||
while let Some(tree) = iter.next() {
|
||||
// Given the parsed tree, if there is a metavar and we are expecting matchers, actually
|
||||
// parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
|
||||
let tree = parse_tree(tree, &mut trees, parsing_patterns, sess, node_id, features, edition);
|
||||
let tree = parse_tree(tree, &mut iter, parsing_patterns, sess, node_id, features, edition);
|
||||
match tree {
|
||||
TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
|
||||
// Not consuming the next token immediately, as it may not be a colon
|
||||
let span = match trees.peek() {
|
||||
let span = match iter.peek() {
|
||||
Some(&tokenstream::TokenTree::Token(
|
||||
Token { kind: token::Colon, span: colon_span },
|
||||
_,
|
||||
)) => {
|
||||
// Consume the colon first
|
||||
trees.next();
|
||||
iter.next();
|
||||
|
||||
// It's ok to consume the next tree no matter how,
|
||||
// since if it's not a token then it will be an invalid declaration.
|
||||
match trees.next() {
|
||||
match iter.next() {
|
||||
Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() {
|
||||
Some((fragment, _)) => {
|
||||
let span = token.span.with_lo(start_sp.lo());
|
||||
|
|
@ -142,14 +143,14 @@ fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Sess
|
|||
/// # Parameters
|
||||
///
|
||||
/// - `tree`: the tree we wish to convert.
|
||||
/// - `outer_trees`: an iterator over trees. We may need to read more tokens from it in order to finish
|
||||
/// - `outer_iter`: an iterator over trees. We may need to read more tokens from it in order to finish
|
||||
/// converting `tree`
|
||||
/// - `parsing_patterns`: same as [parse].
|
||||
/// - `sess`: the parsing session. Any errors will be emitted to this session.
|
||||
/// - `features`: language features so we can do feature gating.
|
||||
fn parse_tree<'a>(
|
||||
tree: &'a tokenstream::TokenTree,
|
||||
outer_trees: &mut impl Iterator<Item = &'a tokenstream::TokenTree>,
|
||||
outer_iter: &mut TokenStreamIter<'a>,
|
||||
parsing_patterns: bool,
|
||||
sess: &Session,
|
||||
node_id: NodeId,
|
||||
|
|
@ -162,15 +163,16 @@ fn parse_tree<'a>(
|
|||
&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span: dollar_span }, _) => {
|
||||
// FIXME: Handle `Invisible`-delimited groups in a more systematic way
|
||||
// during parsing.
|
||||
let mut next = outer_trees.next();
|
||||
let mut trees: Box<dyn Iterator<Item = &tokenstream::TokenTree>>;
|
||||
match next {
|
||||
let mut next = outer_iter.next();
|
||||
let mut iter_storage;
|
||||
let mut iter: &mut TokenStreamIter<'_> = match next {
|
||||
Some(tokenstream::TokenTree::Delimited(.., delim, tts)) if delim.skip() => {
|
||||
trees = Box::new(tts.trees());
|
||||
next = trees.next();
|
||||
iter_storage = tts.iter();
|
||||
next = iter_storage.next();
|
||||
&mut iter_storage
|
||||
}
|
||||
_ => trees = Box::new(outer_trees),
|
||||
}
|
||||
_ => outer_iter,
|
||||
};
|
||||
|
||||
match next {
|
||||
// `tree` is followed by a delimited set of token trees.
|
||||
|
|
@ -229,7 +231,7 @@ fn parse_tree<'a>(
|
|||
let sequence = parse(tts, parsing_patterns, sess, node_id, features, edition);
|
||||
// Get the Kleene operator and optional separator
|
||||
let (separator, kleene) =
|
||||
parse_sep_and_kleene_op(&mut trees, delim_span.entire(), sess);
|
||||
parse_sep_and_kleene_op(&mut iter, delim_span.entire(), sess);
|
||||
// Count the number of captured "names" (i.e., named metavars)
|
||||
let num_captures =
|
||||
if parsing_patterns { count_metavar_decls(&sequence) } else { 0 };
|
||||
|
|
@ -312,11 +314,11 @@ fn kleene_op(token: &Token) -> Option<KleeneOp> {
|
|||
/// - Ok(Ok((op, span))) if the next token tree is a KleeneOp
|
||||
/// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp
|
||||
/// - Err(span) if the next token tree is not a token
|
||||
fn parse_kleene_op<'a>(
|
||||
input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>,
|
||||
fn parse_kleene_op(
|
||||
iter: &mut TokenStreamIter<'_>,
|
||||
span: Span,
|
||||
) -> Result<Result<(KleeneOp, Span), Token>, Span> {
|
||||
match input.next() {
|
||||
match iter.next() {
|
||||
Some(tokenstream::TokenTree::Token(token, _)) => match kleene_op(token) {
|
||||
Some(op) => Ok(Ok((op, token.span))),
|
||||
None => Ok(Err(token.clone())),
|
||||
|
|
@ -333,22 +335,22 @@ fn parse_kleene_op<'a>(
|
|||
/// itself. Note that here we are parsing the _macro_ itself, rather than trying to match some
|
||||
/// stream of tokens in an invocation of a macro.
|
||||
///
|
||||
/// This function will take some input iterator `input` corresponding to `span` and a parsing
|
||||
/// session `sess`. If the next one (or possibly two) tokens in `input` correspond to a Kleene
|
||||
/// This function will take some input iterator `iter` corresponding to `span` and a parsing
|
||||
/// session `sess`. If the next one (or possibly two) tokens in `iter` correspond to a Kleene
|
||||
/// operator and separator, then a tuple with `(separator, KleeneOp)` is returned. Otherwise, an
|
||||
/// error with the appropriate span is emitted to `sess` and a dummy value is returned.
|
||||
fn parse_sep_and_kleene_op<'a>(
|
||||
input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>,
|
||||
fn parse_sep_and_kleene_op(
|
||||
iter: &mut TokenStreamIter<'_>,
|
||||
span: Span,
|
||||
sess: &Session,
|
||||
) -> (Option<Token>, KleeneToken) {
|
||||
// We basically look at two token trees here, denoted as #1 and #2 below
|
||||
let span = match parse_kleene_op(input, span) {
|
||||
let span = match parse_kleene_op(iter, span) {
|
||||
// #1 is a `?`, `+`, or `*` KleeneOp
|
||||
Ok(Ok((op, span))) => return (None, KleeneToken::new(op, span)),
|
||||
|
||||
// #1 is a separator followed by #2, a KleeneOp
|
||||
Ok(Err(token)) => match parse_kleene_op(input, token.span) {
|
||||
Ok(Err(token)) => match parse_kleene_op(iter, token.span) {
|
||||
// #2 is the `?` Kleene op, which does not take a separator (error)
|
||||
Ok(Ok((KleeneOp::ZeroOrOne, span))) => {
|
||||
// Error!
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ use std::iter::once;
|
|||
use std::path::{self, Path, PathBuf};
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans, token};
|
||||
use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans};
|
||||
use rustc_errors::{Diag, ErrorGuaranteed};
|
||||
use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, validate_attr};
|
||||
use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal, validate_attr};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::{Ident, Span, sym};
|
||||
|
|
@ -70,7 +70,7 @@ pub(crate) fn parse_external_mod(
|
|||
let mut parser =
|
||||
unwrap_or_emit_fatal(new_parser_from_file(&sess.psess, &mp.file_path, Some(span)));
|
||||
let (inner_attrs, items, inner_span) =
|
||||
parser.parse_mod(&token::Eof).map_err(|err| ModError::ParserError(err))?;
|
||||
parser.parse_mod(exp!(Eof)).map_err(|err| ModError::ParserError(err))?;
|
||||
attrs.extend(inner_attrs);
|
||||
(items, inner_span, mp.file_path)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use rustc_data_structures::sync::Lrc;
|
|||
use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult};
|
||||
use rustc_parse::lexer::nfc_normalize;
|
||||
use rustc_parse::parser::Parser;
|
||||
use rustc_parse::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal};
|
||||
use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal};
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::def_id::CrateNum;
|
||||
use rustc_span::{BytePos, FileName, Pos, SourceFile, Span, Symbol, sym};
|
||||
|
|
@ -111,9 +111,9 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
|
|||
// Estimate the capacity as `stream.len()` rounded up to the next power
|
||||
// of two to limit the number of required reallocations.
|
||||
let mut trees = Vec::with_capacity(stream.len().next_power_of_two());
|
||||
let mut cursor = stream.trees();
|
||||
let mut iter = stream.iter();
|
||||
|
||||
while let Some(tree) = cursor.next() {
|
||||
while let Some(tree) = iter.next() {
|
||||
let (Token { kind, span }, joint) = match tree.clone() {
|
||||
tokenstream::TokenTree::Delimited(span, _, delim, tts) => {
|
||||
let delimiter = pm::Delimiter::from_internal(delim);
|
||||
|
|
@ -473,7 +473,7 @@ impl server::FreeFunctions for Rustc<'_, '_> {
|
|||
unwrap_or_emit_fatal(new_parser_from_source_str(self.psess(), name, s.to_owned()));
|
||||
|
||||
let first_span = parser.token.span.data();
|
||||
let minus_present = parser.eat(&token::BinOp(token::Minus));
|
||||
let minus_present = parser.eat(exp!(Minus));
|
||||
|
||||
let lit_span = parser.token.span.data();
|
||||
let token::Literal(mut lit) = parser.token.kind else {
|
||||
|
|
|
|||
|
|
@ -178,6 +178,8 @@ declare_features! (
|
|||
(accepted, destructuring_assignment, "1.59.0", Some(71126)),
|
||||
/// Allows using the `#[diagnostic]` attribute tool namespace
|
||||
(accepted, diagnostic_namespace, "1.78.0", Some(111996)),
|
||||
/// Controls errors in trait implementations.
|
||||
(accepted, do_not_recommend, "CURRENT_RUSTC_VERSION", Some(51992)),
|
||||
/// Allows `#[doc(alias = "...")]`.
|
||||
(accepted, doc_alias, "1.48.0", Some(50146)),
|
||||
/// Allows `..` in tuple (struct) patterns.
|
||||
|
|
|
|||
|
|
@ -1187,10 +1187,9 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>>
|
|||
map
|
||||
});
|
||||
|
||||
pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool {
|
||||
pub fn is_stable_diagnostic_attribute(sym: Symbol, _features: &Features) -> bool {
|
||||
match sym {
|
||||
sym::on_unimplemented => true,
|
||||
sym::do_not_recommend => features.do_not_recommend(),
|
||||
sym::on_unimplemented | sym::do_not_recommend => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -462,8 +462,6 @@ declare_features! (
|
|||
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
|
||||
/// Allows deref patterns.
|
||||
(incomplete, deref_patterns, "1.79.0", Some(87121)),
|
||||
/// Controls errors in trait implementations.
|
||||
(unstable, do_not_recommend, "1.67.0", Some(51992)),
|
||||
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
|
||||
(unstable, doc_auto_cfg, "1.58.0", Some(43781)),
|
||||
/// Allows `#[doc(cfg(...))]`.
|
||||
|
|
|
|||
|
|
@ -430,12 +430,12 @@ fn compare_method_predicate_entailment<'tcx>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
struct RemapLateBound<'a, 'tcx> {
|
||||
struct RemapLateParam<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mapping: &'a FxIndexMap<ty::BoundRegionKind, ty::BoundRegionKind>,
|
||||
mapping: &'a FxIndexMap<ty::LateParamRegionKind, ty::LateParamRegionKind>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateBound<'_, 'tcx> {
|
||||
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateParam<'_, 'tcx> {
|
||||
fn cx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
|
@ -445,7 +445,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateBound<'_, 'tcx> {
|
|||
ty::Region::new_late_param(
|
||||
self.tcx,
|
||||
fr.scope,
|
||||
self.mapping.get(&fr.bound_region).copied().unwrap_or(fr.bound_region),
|
||||
self.mapping.get(&fr.kind).copied().unwrap_or(fr.kind),
|
||||
)
|
||||
} else {
|
||||
r
|
||||
|
|
|
|||
|
|
@ -289,11 +289,16 @@ fn report_mismatched_rpitit_signature<'tcx>(
|
|||
tcx.fn_sig(trait_m_def_id).skip_binder().bound_vars(),
|
||||
tcx.fn_sig(impl_m_def_id).skip_binder().bound_vars(),
|
||||
)
|
||||
.filter_map(|(impl_bv, trait_bv)| {
|
||||
.enumerate()
|
||||
.filter_map(|(idx, (impl_bv, trait_bv))| {
|
||||
if let ty::BoundVariableKind::Region(impl_bv) = impl_bv
|
||||
&& let ty::BoundVariableKind::Region(trait_bv) = trait_bv
|
||||
{
|
||||
Some((impl_bv, trait_bv))
|
||||
let var = ty::BoundVar::from_usize(idx);
|
||||
Some((
|
||||
ty::LateParamRegionKind::from_bound(var, impl_bv),
|
||||
ty::LateParamRegionKind::from_bound(var, trait_bv),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -301,7 +306,7 @@ fn report_mismatched_rpitit_signature<'tcx>(
|
|||
.collect();
|
||||
|
||||
let mut return_ty =
|
||||
trait_m_sig.output().fold_with(&mut super::RemapLateBound { tcx, mapping: &mapping });
|
||||
trait_m_sig.output().fold_with(&mut super::RemapLateParam { tcx, mapping: &mapping });
|
||||
|
||||
if tcx.asyncness(impl_m_def_id).is_async() && tcx.asyncness(trait_m_def_id).is_async() {
|
||||
let ty::Alias(ty::Projection, future_ty) = return_ty.kind() else {
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
|||
let mut prev_cx = visitor.cx;
|
||||
|
||||
visitor.enter_scope(Scope {
|
||||
id: blk.hir_id.local_id,
|
||||
local_id: blk.hir_id.local_id,
|
||||
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
|
||||
});
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
|
|
@ -154,7 +154,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
|||
// the first such subscope, which has the block itself as a
|
||||
// parent.
|
||||
visitor.enter_scope(Scope {
|
||||
id: blk.hir_id.local_id,
|
||||
local_id: blk.hir_id.local_id,
|
||||
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
|
||||
});
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
|
|
@ -184,7 +184,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
|||
visitor
|
||||
.scope_tree
|
||||
.backwards_incompatible_scope
|
||||
.insert(local_id, Scope { id: local_id, data: ScopeData::Node });
|
||||
.insert(local_id, Scope { local_id, data: ScopeData::Node });
|
||||
}
|
||||
visitor.visit_expr(tail_expr);
|
||||
}
|
||||
|
|
@ -221,7 +221,7 @@ fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir
|
|||
}
|
||||
|
||||
fn resolve_pat<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
|
||||
visitor.record_child_scope(Scope { id: pat.hir_id.local_id, data: ScopeData::Node });
|
||||
visitor.record_child_scope(Scope { local_id: pat.hir_id.local_id, data: ScopeData::Node });
|
||||
|
||||
// If this is a binding then record the lifetime of that binding.
|
||||
if let PatKind::Binding(..) = pat.kind {
|
||||
|
|
@ -485,7 +485,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
|||
} else {
|
||||
ScopeData::IfThen
|
||||
};
|
||||
visitor.enter_scope(Scope { id: then.hir_id.local_id, data });
|
||||
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
visitor.visit_expr(cond);
|
||||
visitor.visit_expr(then);
|
||||
|
|
@ -500,7 +500,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
|||
} else {
|
||||
ScopeData::IfThen
|
||||
};
|
||||
visitor.enter_scope(Scope { id: then.hir_id.local_id, data });
|
||||
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
visitor.visit_expr(cond);
|
||||
visitor.visit_expr(then);
|
||||
|
|
@ -516,7 +516,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
|||
|
||||
if let hir::ExprKind::Yield(_, source) = &expr.kind {
|
||||
// Mark this expr's scope and all parent scopes as containing `yield`.
|
||||
let mut scope = Scope { id: expr.hir_id.local_id, data: ScopeData::Node };
|
||||
let mut scope = Scope { local_id: expr.hir_id.local_id, data: ScopeData::Node };
|
||||
loop {
|
||||
let span = match expr.kind {
|
||||
hir::ExprKind::Yield(expr, hir::YieldSource::Await { .. }) => {
|
||||
|
|
@ -803,9 +803,9 @@ impl<'tcx> RegionResolutionVisitor<'tcx> {
|
|||
// account for the destruction scope representing the scope of
|
||||
// the destructors that run immediately after it completes.
|
||||
if self.terminating_scopes.contains(&id) {
|
||||
self.enter_scope(Scope { id, data: ScopeData::Destruction });
|
||||
self.enter_scope(Scope { local_id: id, data: ScopeData::Destruction });
|
||||
}
|
||||
self.enter_scope(Scope { id, data: ScopeData::Node });
|
||||
self.enter_scope(Scope { local_id: id, data: ScopeData::Node });
|
||||
}
|
||||
|
||||
fn enter_body(&mut self, hir_id: hir::HirId, f: impl FnOnce(&mut Self)) {
|
||||
|
|
@ -822,8 +822,8 @@ impl<'tcx> RegionResolutionVisitor<'tcx> {
|
|||
let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
|
||||
self.terminating_scopes.insert(hir_id.local_id);
|
||||
|
||||
self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::CallSite });
|
||||
self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::Arguments });
|
||||
self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::CallSite });
|
||||
self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::Arguments });
|
||||
|
||||
f(self);
|
||||
|
||||
|
|
|
|||
|
|
@ -2339,8 +2339,11 @@ fn lint_redundant_lifetimes<'tcx>(
|
|||
);
|
||||
// If we are in a function, add its late-bound lifetimes too.
|
||||
if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {
|
||||
for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() {
|
||||
for (idx, var) in
|
||||
tcx.fn_sig(owner_id).instantiate_identity().bound_vars().iter().enumerate()
|
||||
{
|
||||
let ty::BoundVariableKind::Region(kind) = var else { continue };
|
||||
let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind);
|
||||
lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -371,10 +371,9 @@ pub(super) fn explicit_item_bounds_with_filter(
|
|||
associated_type_bounds(tcx, def_id, opaque_ty.bounds, opaque_ty.span, filter);
|
||||
return ty::EarlyBinder::bind(bounds);
|
||||
}
|
||||
Some(ty::ImplTraitInTraitData::Impl { .. }) => span_bug!(
|
||||
tcx.def_span(def_id),
|
||||
"item bounds for RPITIT in impl to be fed on def-id creation"
|
||||
),
|
||||
Some(ty::ImplTraitInTraitData::Impl { .. }) => {
|
||||
span_bug!(tcx.def_span(def_id), "RPITIT in impl should not have item bounds")
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -956,6 +956,15 @@ pub(super) fn const_conditions<'tcx>(
|
|||
bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}");
|
||||
}
|
||||
|
||||
match tcx.opt_rpitit_info(def_id.to_def_id()) {
|
||||
// RPITITs inherit const conditions of their parent fn
|
||||
Some(
|
||||
ty::ImplTraitInTraitData::Impl { fn_def_id }
|
||||
| ty::ImplTraitInTraitData::Trait { fn_def_id, .. },
|
||||
) => return tcx.const_conditions(fn_def_id),
|
||||
None => {}
|
||||
}
|
||||
|
||||
let (generics, trait_def_id_and_supertraits, has_parent) = match tcx.hir_node_by_def_id(def_id)
|
||||
{
|
||||
Node::Item(item) => match item.kind {
|
||||
|
|
@ -1059,19 +1068,29 @@ pub(super) fn explicit_implied_const_bounds<'tcx>(
|
|||
bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}");
|
||||
}
|
||||
|
||||
let bounds = match tcx.hir_node_by_def_id(def_id) {
|
||||
Node::Item(hir::Item { kind: hir::ItemKind::Trait(..), .. }) => {
|
||||
implied_predicates_with_filter(
|
||||
tcx,
|
||||
def_id.to_def_id(),
|
||||
PredicateFilter::SelfConstIfConst,
|
||||
)
|
||||
}
|
||||
Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. })
|
||||
| Node::OpaqueTy(_) => {
|
||||
let bounds = match tcx.opt_rpitit_info(def_id.to_def_id()) {
|
||||
// RPITIT's bounds are the same as opaque type bounds, but with
|
||||
// a projection self type.
|
||||
Some(ty::ImplTraitInTraitData::Trait { .. }) => {
|
||||
explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst)
|
||||
}
|
||||
_ => bug!("explicit_implied_const_bounds called on wrong item: {def_id:?}"),
|
||||
Some(ty::ImplTraitInTraitData::Impl { .. }) => {
|
||||
span_bug!(tcx.def_span(def_id), "RPITIT in impl should not have item bounds")
|
||||
}
|
||||
None => match tcx.hir_node_by_def_id(def_id) {
|
||||
Node::Item(hir::Item { kind: hir::ItemKind::Trait(..), .. }) => {
|
||||
implied_predicates_with_filter(
|
||||
tcx,
|
||||
def_id.to_def_id(),
|
||||
PredicateFilter::SelfConstIfConst,
|
||||
)
|
||||
}
|
||||
Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. })
|
||||
| Node::OpaqueTy(_) => {
|
||||
explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst)
|
||||
}
|
||||
_ => bug!("explicit_implied_const_bounds called on wrong item: {def_id:?}"),
|
||||
},
|
||||
};
|
||||
|
||||
bounds.map_bound(|bounds| {
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
ty::Region::new_late_param(
|
||||
tcx,
|
||||
scope.to_def_id(),
|
||||
ty::BoundRegionKind::Named(id.to_def_id(), name),
|
||||
ty::LateParamRegionKind::Named(id.to_def_id(), name),
|
||||
)
|
||||
|
||||
// (*) -- not late-bound, won't change
|
||||
|
|
|
|||
|
|
@ -1315,43 +1315,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Then try to coerce the previous expressions to the type of the new one.
|
||||
// This requires ensuring there are no coercions applied to *any* of the
|
||||
// previous expressions, other than noop reborrows (ignoring lifetimes).
|
||||
for expr in exprs {
|
||||
let expr = expr.as_coercion_site();
|
||||
let noop = match self.typeck_results.borrow().expr_adjustments(expr) {
|
||||
&[
|
||||
Adjustment { kind: Adjust::Deref(_), .. },
|
||||
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl_adj)), .. },
|
||||
] => {
|
||||
match *self.node_ty(expr.hir_id).kind() {
|
||||
ty::Ref(_, _, mt_orig) => {
|
||||
let mutbl_adj: hir::Mutability = mutbl_adj.into();
|
||||
// Reborrow that we can safely ignore, because
|
||||
// the next adjustment can only be a Deref
|
||||
// which will be merged into it.
|
||||
mutbl_adj == mt_orig
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
&[Adjustment { kind: Adjust::NeverToAny, .. }] | &[] => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !noop {
|
||||
debug!(
|
||||
"coercion::try_find_coercion_lub: older expression {:?} had adjustments, requiring LUB",
|
||||
expr,
|
||||
);
|
||||
|
||||
return Err(self
|
||||
.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
|
||||
.unwrap_err());
|
||||
}
|
||||
}
|
||||
|
||||
match self.commit_if_ok(|_| coerce.coerce(prev_ty, new_ty)) {
|
||||
Err(_) => {
|
||||
// Avoid giving strange errors on failed attempts.
|
||||
|
|
|
|||
|
|
@ -146,18 +146,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag());
|
||||
let mut typeck = self.typeck_results.borrow_mut();
|
||||
let mut node_ty = typeck.node_types_mut();
|
||||
if let Some(ty) = node_ty.get(id)
|
||||
&& let Err(e) = ty.error_reported()
|
||||
{
|
||||
// Do not overwrite nodes that were already marked as `{type error}`. This allows us to
|
||||
// silence unnecessary errors from obligations that were set earlier than a type error
|
||||
// was produced, but that is overwritten by later analysis. This happens in particular
|
||||
// for `Sized` obligations introduced in gather_locals. (#117846)
|
||||
self.set_tainted_by_errors(e);
|
||||
return;
|
||||
}
|
||||
|
||||
node_ty.insert(id, ty);
|
||||
if let Some(prev) = node_ty.insert(id, ty) {
|
||||
if prev.references_error() {
|
||||
node_ty.insert(id, prev);
|
||||
} else if !ty.references_error() {
|
||||
// Could change this to a bug, but there's lots of diagnostic code re-lowering
|
||||
// or re-typechecking nodes that were already typecked.
|
||||
// Lots of that diagnostics code relies on subtle effects of re-lowering, so we'll
|
||||
// let it keep doing that and just ensure that compilation won't succeed.
|
||||
self.dcx().span_delayed_bug(
|
||||
self.tcx.hir().span(id),
|
||||
format!("`{prev}` overridden by `{ty}` for {id:?} in {:?}", self.body_id),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = ty.error_reported() {
|
||||
self.set_tainted_by_errors(e);
|
||||
|
|
@ -1104,7 +1107,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if let Res::Local(hid) = res {
|
||||
let ty = self.local_ty(span, hid);
|
||||
let ty = self.normalize(span, ty);
|
||||
self.write_ty(hir_id, ty);
|
||||
return (ty, res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1750,10 +1750,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
|
||||
pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) -> Ty<'tcx> {
|
||||
// Determine and write the type which we'll check the pattern against.
|
||||
let decl_ty = self.local_ty(decl.span, decl.hir_id);
|
||||
self.write_ty(decl.hir_id, decl_ty);
|
||||
|
||||
// Type check the initializer.
|
||||
if let Some(ref init) = decl.init {
|
||||
|
|
@ -1785,11 +1784,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
self.diverges.set(previous_diverges);
|
||||
}
|
||||
decl_ty
|
||||
}
|
||||
|
||||
/// Type check a `let` statement.
|
||||
fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) {
|
||||
self.check_decl(local.into());
|
||||
let ty = self.check_decl(local.into());
|
||||
self.write_ty(local.hir_id, ty);
|
||||
if local.pat.is_never_pattern() {
|
||||
self.diverges.set(Diverges::Always {
|
||||
span: local.pat.span,
|
||||
|
|
|
|||
|
|
@ -717,12 +717,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
BindingMode(def_br, Mutability::Mut)
|
||||
} else {
|
||||
// `mut` resets the binding mode on edition <= 2021
|
||||
*self
|
||||
.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.entry(pat_info.top_info.hir_id)
|
||||
.or_default() |= pat.span.at_least_rust_2024();
|
||||
self.add_rust_2024_migration_desugared_pat(
|
||||
pat_info.top_info.hir_id,
|
||||
pat.span,
|
||||
ident.span,
|
||||
"requires binding by-value, but the implicit default is by-reference",
|
||||
);
|
||||
BindingMode(ByRef::No, Mutability::Mut)
|
||||
}
|
||||
}
|
||||
|
|
@ -730,12 +730,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
BindingMode(ByRef::Yes(_), _) => {
|
||||
if matches!(def_br, ByRef::Yes(_)) {
|
||||
// `ref`/`ref mut` overrides the binding mode on edition <= 2021
|
||||
*self
|
||||
.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.entry(pat_info.top_info.hir_id)
|
||||
.or_default() |= pat.span.at_least_rust_2024();
|
||||
self.add_rust_2024_migration_desugared_pat(
|
||||
pat_info.top_info.hir_id,
|
||||
pat.span,
|
||||
ident.span,
|
||||
"cannot override to bind by-reference when that is the implicit default",
|
||||
);
|
||||
}
|
||||
user_bind_annot
|
||||
}
|
||||
|
|
@ -2265,12 +2265,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Reset binding mode on old editions
|
||||
if pat_info.binding_mode != ByRef::No {
|
||||
pat_info.binding_mode = ByRef::No;
|
||||
*self
|
||||
.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.entry(pat_info.top_info.hir_id)
|
||||
.or_default() |= pat.span.at_least_rust_2024();
|
||||
self.add_rust_2024_migration_desugared_pat(
|
||||
pat_info.top_info.hir_id,
|
||||
pat.span,
|
||||
inner.span,
|
||||
"cannot implicitly match against multiple layers of reference",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2629,4 +2629,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
_ => (false, ty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Record a pattern that's invalid under Rust 2024 match ergonomics, along with a problematic
|
||||
/// span, so that the pattern migration lint can desugar it during THIR construction.
|
||||
fn add_rust_2024_migration_desugared_pat(
|
||||
&self,
|
||||
pat_id: HirId,
|
||||
subpat_span: Span,
|
||||
cutoff_span: Span,
|
||||
detailed_label: &str,
|
||||
) {
|
||||
// Try to trim the span we're labeling to just the `&` or binding mode that's an issue.
|
||||
// If the subpattern's span is is from an expansion, the emitted label will not be trimmed.
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
let cutoff_span = source_map
|
||||
.span_extend_prev_while(cutoff_span, char::is_whitespace)
|
||||
.unwrap_or(cutoff_span);
|
||||
// Ensure we use the syntax context and thus edition of `subpat_span`; this will be a hard
|
||||
// error if the subpattern is of edition >= 2024.
|
||||
let trimmed_span = subpat_span.until(cutoff_span).with_ctxt(subpat_span.ctxt());
|
||||
|
||||
// Only provide a detailed label if the problematic subpattern isn't from an expansion.
|
||||
// In the case that it's from a macro, we'll add a more detailed note in the emitter.
|
||||
let desc = if subpat_span.from_expansion() {
|
||||
"default binding mode is reset within expansion"
|
||||
} else {
|
||||
detailed_label
|
||||
};
|
||||
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.entry(pat_id)
|
||||
.or_default()
|
||||
.push((trimmed_span, desc.to_owned()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -766,7 +766,11 @@ fn test_unstable_options_tracking_hash() {
|
|||
})
|
||||
);
|
||||
tracked!(codegen_backend, Some("abc".to_string()));
|
||||
tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc, no_mir_spans: true });
|
||||
tracked!(coverage_options, CoverageOptions {
|
||||
level: CoverageLevel::Mcdc,
|
||||
no_mir_spans: true,
|
||||
discard_all_spans_in_codegen: true
|
||||
});
|
||||
tracked!(crate_attr, vec!["abc".to_string()]);
|
||||
tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
|
||||
tracked!(debug_info_for_profiling, true);
|
||||
|
|
|
|||
|
|
@ -1828,7 +1828,7 @@ impl KeywordIdents {
|
|||
fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: &TokenStream) {
|
||||
// Check if the preceding token is `$`, because we want to allow `$async`, etc.
|
||||
let mut prev_dollar = false;
|
||||
for tt in tokens.trees() {
|
||||
for tt in tokens.iter() {
|
||||
match tt {
|
||||
// Only report non-raw idents.
|
||||
TokenTree::Token(token, _) => {
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
|
|||
// Lazily compute these two, since they're likely a bit expensive.
|
||||
variances: LazyCell::new(|| {
|
||||
let mut functional_variances = FunctionalVariances {
|
||||
tcx: tcx,
|
||||
tcx,
|
||||
variances: FxHashMap::default(),
|
||||
ambient_variance: ty::Covariant,
|
||||
generics: tcx.generics_of(parent_def_id),
|
||||
|
|
@ -325,7 +325,7 @@ where
|
|||
ParamKind::Free(def_id, name) => ty::Region::new_late_param(
|
||||
self.tcx,
|
||||
self.parent_def_id.to_def_id(),
|
||||
ty::BoundRegionKind::Named(def_id, name),
|
||||
ty::LateParamRegionKind::Named(def_id, name),
|
||||
),
|
||||
// Totally ignore late bound args from binders.
|
||||
ParamKind::Late => return true,
|
||||
|
|
@ -475,7 +475,7 @@ fn extract_def_id_from_arg<'tcx>(
|
|||
)
|
||||
| ty::ReLateParam(ty::LateParamRegion {
|
||||
scope: _,
|
||||
bound_region: ty::BoundRegionKind::Named(def_id, ..),
|
||||
kind: ty::LateParamRegionKind::Named(def_id, ..),
|
||||
}) => def_id,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
|
@ -544,7 +544,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for FunctionalVariances<'tcx> {
|
|||
)
|
||||
| ty::ReLateParam(ty::LateParamRegion {
|
||||
scope: _,
|
||||
bound_region: ty::BoundRegionKind::Named(def_id, ..),
|
||||
kind: ty::LateParamRegionKind::Named(def_id, ..),
|
||||
}) => def_id,
|
||||
_ => {
|
||||
return Ok(a);
|
||||
|
|
|
|||
|
|
@ -170,27 +170,11 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
|
|||
| PatKind::TupleStruct(qpath, ..)
|
||||
| PatKind::Struct(qpath, ..),
|
||||
..
|
||||
}) => {
|
||||
if let QPath::TypeRelative(qpath_ty, ..) = qpath
|
||||
&& qpath_ty.hir_id == ty.hir_id
|
||||
{
|
||||
Some(path.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Node::Expr(Expr { kind: ExprKind::Path(qpath), .. }) => {
|
||||
if let QPath::TypeRelative(qpath_ty, ..) = qpath
|
||||
&& qpath_ty.hir_id == ty.hir_id
|
||||
{
|
||||
Some(path.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// Can't unify these two branches because qpath below is `&&` and above is `&`
|
||||
// and `A | B` paths don't play well together with adjustments, apparently.
|
||||
Node::Expr(Expr { kind: ExprKind::Struct(qpath, ..), .. }) => {
|
||||
})
|
||||
| Node::Expr(
|
||||
Expr { kind: ExprKind::Path(qpath), .. }
|
||||
| &Expr { kind: ExprKind::Struct(qpath, ..), .. },
|
||||
) => {
|
||||
if let QPath::TypeRelative(qpath_ty, ..) = qpath
|
||||
&& qpath_ty.hir_id == ty.hir_id
|
||||
{
|
||||
|
|
|
|||
|
|
@ -914,7 +914,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
|
||||
let src = LintLevelSource::Node { name, span: sp, reason };
|
||||
for &id in ids {
|
||||
if self.check_gated_lint(id, attr.span(), false) {
|
||||
if self.check_gated_lint(id, sp, false) {
|
||||
self.insert_spec(id, (level, src));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ impl Expr2024 {
|
|||
let mut prev_colon = false;
|
||||
let mut prev_identifier = false;
|
||||
let mut prev_dollar = false;
|
||||
for tt in tokens.trees() {
|
||||
for tt in tokens.iter() {
|
||||
debug!(
|
||||
"check_tokens: {:?} - colon {prev_dollar} - ident {prev_identifier} - colon {prev_colon}",
|
||||
tt
|
||||
|
|
|
|||
|
|
@ -84,23 +84,23 @@ use crate::ty::TyCtxt;
|
|||
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy, TyEncodable, TyDecodable)]
|
||||
#[derive(HashStable)]
|
||||
pub struct Scope {
|
||||
pub id: hir::ItemLocalId,
|
||||
pub local_id: hir::ItemLocalId,
|
||||
pub data: ScopeData,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Scope {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.data {
|
||||
ScopeData::Node => write!(fmt, "Node({:?})", self.id),
|
||||
ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.id),
|
||||
ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id),
|
||||
ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id),
|
||||
ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.id),
|
||||
ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.id),
|
||||
ScopeData::Node => write!(fmt, "Node({:?})", self.local_id),
|
||||
ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.local_id),
|
||||
ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.local_id),
|
||||
ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.local_id),
|
||||
ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.local_id),
|
||||
ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.local_id),
|
||||
ScopeData::Remainder(fsi) => write!(
|
||||
fmt,
|
||||
"Remainder {{ block: {:?}, first_statement_index: {}}}",
|
||||
self.id,
|
||||
self.local_id,
|
||||
fsi.as_u32(),
|
||||
),
|
||||
}
|
||||
|
|
@ -164,18 +164,8 @@ rustc_index::newtype_index! {
|
|||
rustc_data_structures::static_assert_size!(ScopeData, 4);
|
||||
|
||||
impl Scope {
|
||||
/// Returns an item-local ID associated with this scope.
|
||||
///
|
||||
/// N.B., likely to be replaced as API is refined; e.g., pnkfelix
|
||||
/// anticipates `fn entry_node_id` and `fn each_exit_node_id`.
|
||||
pub fn item_local_id(&self) -> hir::ItemLocalId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option<HirId> {
|
||||
scope_tree
|
||||
.root_body
|
||||
.map(|hir_id| HirId { owner: hir_id.owner, local_id: self.item_local_id() })
|
||||
scope_tree.root_body.map(|hir_id| HirId { owner: hir_id.owner, local_id: self.local_id })
|
||||
}
|
||||
|
||||
/// Returns the span of this `Scope`. Note that in general the
|
||||
|
|
@ -350,7 +340,7 @@ impl ScopeTree {
|
|||
|
||||
pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) {
|
||||
debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime);
|
||||
assert!(var != lifetime.item_local_id());
|
||||
assert!(var != lifetime.local_id);
|
||||
self.var_map.insert(var, lifetime);
|
||||
}
|
||||
|
||||
|
|
@ -359,7 +349,7 @@ impl ScopeTree {
|
|||
match &candidate_type {
|
||||
RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. }
|
||||
| RvalueCandidateType::Pattern { lifetime: Some(lifetime), .. } => {
|
||||
assert!(var.local_id != lifetime.item_local_id())
|
||||
assert!(var.local_id != lifetime.local_id)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,22 +154,6 @@ impl Debug for CoverageKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(TypeFoldable, TypeVisitable)]
|
||||
pub struct SourceRegion {
|
||||
pub start_line: u32,
|
||||
pub start_col: u32,
|
||||
pub end_line: u32,
|
||||
pub end_col: u32,
|
||||
}
|
||||
|
||||
impl Debug for SourceRegion {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
let &Self { start_line, start_col, end_line, end_col } = self;
|
||||
write!(fmt, "{start_line}:{start_col} - {end_line}:{end_col}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
|
||||
#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
|
||||
pub enum Op {
|
||||
|
|
@ -231,7 +215,7 @@ impl MappingKind {
|
|||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct Mapping {
|
||||
pub kind: MappingKind,
|
||||
pub source_region: SourceRegion,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// Stores per-function coverage information attached to a `mir::Body`,
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
/// A discriminant of an uninhabited enum variant is written.
|
||||
UninhabitedEnumVariantWritten(VariantIdx),
|
||||
/// An uninhabited enum variant is projected.
|
||||
UninhabitedEnumVariantRead(VariantIdx),
|
||||
UninhabitedEnumVariantRead(Option<VariantIdx>),
|
||||
/// Trying to set discriminant to the niched variant, but the value does not match.
|
||||
InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
|
||||
/// ABI-incompatible argument types.
|
||||
|
|
|
|||
|
|
@ -1348,8 +1348,8 @@ pub struct BasicBlockData<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> BasicBlockData<'tcx> {
|
||||
pub fn new(terminator: Option<Terminator<'tcx>>) -> BasicBlockData<'tcx> {
|
||||
BasicBlockData { statements: vec![], terminator, is_cleanup: false }
|
||||
pub fn new(terminator: Option<Terminator<'tcx>>, is_cleanup: bool) -> BasicBlockData<'tcx> {
|
||||
BasicBlockData { statements: vec![], terminator, is_cleanup }
|
||||
}
|
||||
|
||||
/// Accessor for terminator.
|
||||
|
|
|
|||
|
|
@ -603,8 +603,8 @@ fn write_function_coverage_info(
|
|||
for (id, expression) in expressions.iter_enumerated() {
|
||||
writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?;
|
||||
}
|
||||
for coverage::Mapping { kind, source_region } in mappings {
|
||||
writeln!(w, "{INDENT}coverage {kind:?} => {source_region:?};")?;
|
||||
for coverage::Mapping { kind, span } in mappings {
|
||||
writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?;
|
||||
}
|
||||
writeln!(w)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -3091,7 +3091,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
return ty::Region::new_late_param(
|
||||
self,
|
||||
new_parent.to_def_id(),
|
||||
ty::BoundRegionKind::Named(
|
||||
ty::LateParamRegionKind::Named(
|
||||
lbv.to_def_id(),
|
||||
self.item_name(lbv.to_def_id()),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -270,7 +270,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
self.instantiate_bound_regions_uncached(value, |br| {
|
||||
ty::Region::new_late_param(self, all_outlive_scope, br.kind)
|
||||
let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind);
|
||||
ty::Region::new_late_param(self, all_outlive_scope, kind)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -734,21 +734,22 @@ where
|
|||
let layout = match this.variants {
|
||||
Variants::Single { index }
|
||||
// If all variants but one are uninhabited, the variant layout is the enum layout.
|
||||
if index == variant_index &&
|
||||
// Don't confuse variants of uninhabited enums with the enum itself.
|
||||
// For more details see https://github.com/rust-lang/rust/issues/69763.
|
||||
this.fields != FieldsShape::Primitive =>
|
||||
if index == variant_index =>
|
||||
{
|
||||
this.layout
|
||||
}
|
||||
|
||||
Variants::Single { index } => {
|
||||
Variants::Single { .. } | Variants::Empty => {
|
||||
// Single-variant and no-variant enums *can* have other variants, but those are
|
||||
// uninhabited. Produce a layout that has the right fields for that variant, so that
|
||||
// the rest of the compiler can project fields etc as usual.
|
||||
|
||||
let tcx = cx.tcx();
|
||||
let typing_env = cx.typing_env();
|
||||
|
||||
// Deny calling for_variant more than once for non-Single enums.
|
||||
if let Ok(original_layout) = tcx.layout_of(typing_env.as_query_input(this.ty)) {
|
||||
assert_eq!(original_layout.variants, Variants::Single { index });
|
||||
assert_eq!(original_layout.variants, this.variants);
|
||||
}
|
||||
|
||||
let fields = match this.ty.kind() {
|
||||
|
|
@ -902,6 +903,7 @@ where
|
|||
),
|
||||
|
||||
ty::Coroutine(def_id, args) => match this.variants {
|
||||
Variants::Empty => unreachable!(),
|
||||
Variants::Single { index } => TyMaybeWithLayout::Ty(
|
||||
args.as_coroutine()
|
||||
.state_tys(def_id, tcx)
|
||||
|
|
@ -927,6 +929,7 @@ where
|
|||
let field = &def.variant(index).fields[FieldIdx::from_usize(i)];
|
||||
TyMaybeWithLayout::Ty(field.ty(tcx, args))
|
||||
}
|
||||
Variants::Empty => panic!("there is no field in Variants::Empty types"),
|
||||
|
||||
// Discriminant field for enums (where applicable).
|
||||
Variants::Multiple { tag, .. } => {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ pub use self::predicate::{
|
|||
TypeOutlivesPredicate,
|
||||
};
|
||||
pub use self::region::{
|
||||
BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, Region, RegionKind, RegionVid,
|
||||
BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region,
|
||||
RegionKind, RegionVid,
|
||||
};
|
||||
pub use self::rvalue_scopes::RvalueScopes;
|
||||
pub use self::sty::{
|
||||
|
|
@ -971,7 +972,7 @@ pub struct ParamEnv<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::inherent::ParamEnv<TyCtxt<'tcx>> for ParamEnv<'tcx> {
|
||||
fn caller_bounds(self) -> impl IntoIterator<Item = ty::Clause<'tcx>> {
|
||||
fn caller_bounds(self) -> impl inherent::SliceLike<Item = ty::Clause<'tcx>> {
|
||||
self.caller_bounds()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2374,8 +2374,8 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
match *region {
|
||||
ty::ReEarlyParam(ref data) => data.has_name(),
|
||||
|
||||
ty::ReLateParam(ty::LateParamRegion { kind, .. }) => kind.is_named(),
|
||||
ty::ReBound(_, ty::BoundRegion { kind: br, .. })
|
||||
| ty::ReLateParam(ty::LateParamRegion { bound_region: br, .. })
|
||||
| ty::RePlaceholder(ty::Placeholder {
|
||||
bound: ty::BoundRegion { kind: br, .. }, ..
|
||||
}) => {
|
||||
|
|
@ -2448,8 +2448,13 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
|
|||
return Ok(());
|
||||
}
|
||||
}
|
||||
ty::ReLateParam(ty::LateParamRegion { kind, .. }) => {
|
||||
if let Some(name) = kind.get_name() {
|
||||
p!(write("{}", name));
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
ty::ReBound(_, ty::BoundRegion { kind: br, .. })
|
||||
| ty::ReLateParam(ty::LateParamRegion { bound_region: br, .. })
|
||||
| ty::RePlaceholder(ty::Placeholder {
|
||||
bound: ty::BoundRegion { kind: br, .. }, ..
|
||||
}) => {
|
||||
|
|
|
|||
|
|
@ -69,9 +69,10 @@ impl<'tcx> Region<'tcx> {
|
|||
pub fn new_late_param(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
scope: DefId,
|
||||
bound_region: ty::BoundRegionKind,
|
||||
kind: LateParamRegionKind,
|
||||
) -> Region<'tcx> {
|
||||
tcx.intern_region(ty::ReLateParam(ty::LateParamRegion { scope, bound_region }))
|
||||
let data = LateParamRegion { scope, kind };
|
||||
tcx.intern_region(ty::ReLateParam(data))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -124,8 +125,8 @@ impl<'tcx> Region<'tcx> {
|
|||
match kind {
|
||||
ty::ReEarlyParam(region) => Region::new_early_param(tcx, region),
|
||||
ty::ReBound(debruijn, region) => Region::new_bound(tcx, debruijn, region),
|
||||
ty::ReLateParam(ty::LateParamRegion { scope, bound_region }) => {
|
||||
Region::new_late_param(tcx, scope, bound_region)
|
||||
ty::ReLateParam(ty::LateParamRegion { scope, kind }) => {
|
||||
Region::new_late_param(tcx, scope, kind)
|
||||
}
|
||||
ty::ReStatic => tcx.lifetimes.re_static,
|
||||
ty::ReVar(vid) => Region::new_var(tcx, vid),
|
||||
|
|
@ -165,7 +166,7 @@ impl<'tcx> Region<'tcx> {
|
|||
match *self {
|
||||
ty::ReEarlyParam(ebr) => Some(ebr.name),
|
||||
ty::ReBound(_, br) => br.kind.get_name(),
|
||||
ty::ReLateParam(fr) => fr.bound_region.get_name(),
|
||||
ty::ReLateParam(fr) => fr.kind.get_name(),
|
||||
ty::ReStatic => Some(kw::StaticLifetime),
|
||||
ty::RePlaceholder(placeholder) => placeholder.bound.kind.get_name(),
|
||||
_ => None,
|
||||
|
|
@ -187,7 +188,7 @@ impl<'tcx> Region<'tcx> {
|
|||
match *self {
|
||||
ty::ReEarlyParam(ebr) => ebr.has_name(),
|
||||
ty::ReBound(_, br) => br.kind.is_named(),
|
||||
ty::ReLateParam(fr) => fr.bound_region.is_named(),
|
||||
ty::ReLateParam(fr) => fr.kind.is_named(),
|
||||
ty::ReStatic => true,
|
||||
ty::ReVar(..) => false,
|
||||
ty::RePlaceholder(placeholder) => placeholder.bound.kind.is_named(),
|
||||
|
|
@ -310,7 +311,7 @@ impl<'tcx> Region<'tcx> {
|
|||
Some(tcx.generics_of(binding_item).region_param(ebr, tcx).def_id)
|
||||
}
|
||||
ty::ReLateParam(ty::LateParamRegion {
|
||||
bound_region: ty::BoundRegionKind::Named(def_id, _),
|
||||
kind: ty::LateParamRegionKind::Named(def_id, _),
|
||||
..
|
||||
}) => Some(def_id),
|
||||
_ => None,
|
||||
|
|
@ -358,7 +359,71 @@ impl std::fmt::Debug for EarlyParamRegion {
|
|||
/// different parameters apart.
|
||||
pub struct LateParamRegion {
|
||||
pub scope: DefId,
|
||||
pub bound_region: BoundRegionKind,
|
||||
pub kind: LateParamRegionKind,
|
||||
}
|
||||
|
||||
/// When liberating bound regions, we map their [`BoundRegionKind`]
|
||||
/// to this as we need to track the index of anonymous regions. We
|
||||
/// otherwise end up liberating multiple bound regions to the same
|
||||
/// late-bound region.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)]
|
||||
#[derive(HashStable)]
|
||||
pub enum LateParamRegionKind {
|
||||
/// An anonymous region parameter for a given fn (&T)
|
||||
///
|
||||
/// Unlike [`BoundRegionKind::Anon`], this tracks the index of the
|
||||
/// liberated bound region.
|
||||
///
|
||||
/// We should ideally never liberate anonymous regions, but do so for the
|
||||
/// sake of diagnostics in `FnCtxt::sig_of_closure_with_expectation`.
|
||||
Anon(u32),
|
||||
|
||||
/// Named region parameters for functions (a in &'a T)
|
||||
///
|
||||
/// The `DefId` is needed to distinguish free regions in
|
||||
/// the event of shadowing.
|
||||
Named(DefId, Symbol),
|
||||
|
||||
/// Anonymous region for the implicit env pointer parameter
|
||||
/// to a closure
|
||||
ClosureEnv,
|
||||
}
|
||||
|
||||
impl LateParamRegionKind {
|
||||
pub fn from_bound(var: BoundVar, br: BoundRegionKind) -> LateParamRegionKind {
|
||||
match br {
|
||||
BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()),
|
||||
BoundRegionKind::Named(def_id, name) => LateParamRegionKind::Named(def_id, name),
|
||||
BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_named(&self) -> bool {
|
||||
match *self {
|
||||
LateParamRegionKind::Named(_, name) => {
|
||||
name != kw::UnderscoreLifetime && name != kw::Empty
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> Option<Symbol> {
|
||||
if self.is_named() {
|
||||
match *self {
|
||||
LateParamRegionKind::Named(_, name) => return Some(name),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> Option<DefId> {
|
||||
match *self {
|
||||
LateParamRegionKind::Named(id, _) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)]
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ impl RvalueScopes {
|
|||
// if there's one. Static items, for instance, won't
|
||||
// have an enclosing scope, hence no scope will be
|
||||
// returned.
|
||||
let mut id = Scope { id: expr_id, data: ScopeData::Node };
|
||||
let mut id = Scope { local_id: expr_id, data: ScopeData::Node };
|
||||
let mut backwards_incompatible = None;
|
||||
|
||||
while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) {
|
||||
|
|
@ -60,7 +60,7 @@ impl RvalueScopes {
|
|||
if backwards_incompatible.is_none() {
|
||||
backwards_incompatible = region_scope_tree
|
||||
.backwards_incompatible_scope
|
||||
.get(&p.item_local_id())
|
||||
.get(&p.local_id)
|
||||
.copied();
|
||||
}
|
||||
id = p
|
||||
|
|
@ -76,7 +76,7 @@ impl RvalueScopes {
|
|||
pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
|
||||
debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})");
|
||||
if let Some(lifetime) = lifetime {
|
||||
assert!(var != lifetime.item_local_id());
|
||||
assert!(var != lifetime.local_id);
|
||||
}
|
||||
self.map.insert(var, lifetime);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,23 @@ impl fmt::Debug for ty::BoundRegionKind {
|
|||
|
||||
impl fmt::Debug for ty::LateParamRegion {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region)
|
||||
write!(f, "ReLateParam({:?}, {:?})", self.scope, self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ty::LateParamRegionKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
ty::LateParamRegionKind::Anon(idx) => write!(f, "BrAnon({idx})"),
|
||||
ty::LateParamRegionKind::Named(did, name) => {
|
||||
if did.is_crate_root() {
|
||||
write!(f, "BrNamed({name})")
|
||||
} else {
|
||||
write!(f, "BrNamed({did:?}, {name})")
|
||||
}
|
||||
}
|
||||
ty::LateParamRegionKind::ClosureEnv => write!(f, "BrEnv"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,9 +74,8 @@ pub struct TypeckResults<'tcx> {
|
|||
pat_binding_modes: ItemLocalMap<BindingMode>,
|
||||
|
||||
/// Top-level patterns whose match ergonomics need to be desugared by the Rust 2021 -> 2024
|
||||
/// migration lint. The boolean indicates whether the emitted diagnostic should be a hard error
|
||||
/// (if any of the incompatible pattern elements are in edition 2024).
|
||||
rust_2024_migration_desugared_pats: ItemLocalMap<bool>,
|
||||
/// migration lint. Problematic subpatterns are stored in the `Vec` for the lint to highlight.
|
||||
rust_2024_migration_desugared_pats: ItemLocalMap<Vec<(Span, String)>>,
|
||||
|
||||
/// Stores the types which were implicitly dereferenced in pattern binding modes
|
||||
/// for later usage in THIR lowering. For example,
|
||||
|
|
@ -419,14 +418,18 @@ impl<'tcx> TypeckResults<'tcx> {
|
|||
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
|
||||
}
|
||||
|
||||
pub fn rust_2024_migration_desugared_pats(&self) -> LocalTableInContext<'_, bool> {
|
||||
pub fn rust_2024_migration_desugared_pats(
|
||||
&self,
|
||||
) -> LocalTableInContext<'_, Vec<(Span, String)>> {
|
||||
LocalTableInContext {
|
||||
hir_owner: self.hir_owner,
|
||||
data: &self.rust_2024_migration_desugared_pats,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalTableInContextMut<'_, bool> {
|
||||
pub fn rust_2024_migration_desugared_pats_mut(
|
||||
&mut self,
|
||||
) -> LocalTableInContextMut<'_, Vec<(Span, String)>> {
|
||||
LocalTableInContextMut {
|
||||
hir_owner: self.hir_owner,
|
||||
data: &mut self.rust_2024_migration_desugared_pats,
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from
|
|||
|
||||
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
|
||||
|
||||
mir_build_rust_2024_incompatible_pat = patterns are not allowed to reset the default binding mode in edition 2024
|
||||
mir_build_rust_2024_incompatible_pat = this pattern relies on behavior which may change in edition 2024
|
||||
|
||||
mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
|
||||
.attributes = no other attributes may be applied
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ impl<'tcx> CFG<'tcx> {
|
|||
// it as #[inline(never)] to keep rustc's stack use in check.
|
||||
#[inline(never)]
|
||||
pub(crate) fn start_new_block(&mut self) -> BasicBlock {
|
||||
self.basic_blocks.push(BasicBlockData::new(None))
|
||||
self.basic_blocks.push(BasicBlockData::new(None, false))
|
||||
}
|
||||
|
||||
pub(crate) fn start_new_cleanup_block(&mut self) -> BasicBlock {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ pub(super) fn build_custom_mir<'tcx>(
|
|||
};
|
||||
|
||||
body.local_decls.push(LocalDecl::new(return_ty, return_ty_span));
|
||||
body.basic_blocks_mut().push(BasicBlockData::new(None));
|
||||
body.basic_blocks_mut().push(BasicBlockData::new(None, false));
|
||||
body.source_scopes.push(SourceScopeData {
|
||||
span,
|
||||
parent_scope: None,
|
||||
|
|
|
|||
|
|
@ -199,10 +199,12 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
|
|||
match &self.thir[stmt].kind {
|
||||
StmtKind::Let { pattern, initializer: Some(initializer), .. } => {
|
||||
let (var, ..) = self.parse_var(pattern)?;
|
||||
let mut data = BasicBlockData::new(None);
|
||||
data.is_cleanup = parse_by_kind!(self, *initializer, _, "basic block declaration",
|
||||
@variant(mir_basic_block, Normal) => false,
|
||||
@variant(mir_basic_block, Cleanup) => true,
|
||||
let data = BasicBlockData::new(
|
||||
None,
|
||||
parse_by_kind!(self, *initializer, _, "basic block declaration",
|
||||
@variant(mir_basic_block, Normal) => false,
|
||||
@variant(mir_basic_block, Cleanup) => true,
|
||||
),
|
||||
);
|
||||
let block = self.body.basic_blocks_mut().push(data);
|
||||
self.block_map.insert(var, block);
|
||||
|
|
@ -308,8 +310,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
|
|||
ExprKind::Block { block } => &self.thir[*block],
|
||||
);
|
||||
|
||||
let mut data = BasicBlockData::new(None);
|
||||
data.is_cleanup = is_cleanup;
|
||||
let mut data = BasicBlockData::new(None, is_cleanup);
|
||||
for stmt_id in &*block.stmts {
|
||||
let stmt = self.statement_as_expr(*stmt_id)?;
|
||||
let span = self.thir[stmt].span;
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
LocalInfo::BlockTailTemp(tail_info)
|
||||
}
|
||||
|
||||
_ if let Some(Scope { data: ScopeData::IfThenRescope, id }) =
|
||||
_ if let Some(Scope { data: ScopeData::IfThenRescope, local_id }) =
|
||||
temp_lifetime.temp_lifetime =>
|
||||
{
|
||||
LocalInfo::IfThenRescopeTemp {
|
||||
if_then: HirId { owner: this.hir_id.owner, local_id: id },
|
||||
if_then: HirId { owner: this.hir_id.owner, local_id },
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,10 +56,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
pub(crate) fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
let ty = place.ty(&self.local_decls, tcx).ty;
|
||||
if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty) {
|
||||
Operand::Move(place)
|
||||
} else {
|
||||
if self.infcx.type_is_copy_modulo_regions(self.param_env, ty) {
|
||||
Operand::Copy(place)
|
||||
} else {
|
||||
Operand::Move(place)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -531,9 +531,9 @@ fn construct_fn<'tcx>(
|
|||
);
|
||||
|
||||
let call_site_scope =
|
||||
region::Scope { id: body.id().hir_id.local_id, data: region::ScopeData::CallSite };
|
||||
region::Scope { local_id: body.id().hir_id.local_id, data: region::ScopeData::CallSite };
|
||||
let arg_scope =
|
||||
region::Scope { id: body.id().hir_id.local_id, data: region::ScopeData::Arguments };
|
||||
region::Scope { local_id: body.id().hir_id.local_id, data: region::ScopeData::Arguments };
|
||||
let source_info = builder.source_info(span);
|
||||
let call_site_s = (call_site_scope, source_info);
|
||||
let _: BlockAnd<()> = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ use rustc_index::{IndexSlice, IndexVec};
|
|||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::{ExprId, LintLevel};
|
||||
use rustc_middle::{bug, span_bug, ty};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
|
|
@ -151,15 +151,13 @@ struct DropData {
|
|||
|
||||
/// Whether this is a value Drop or a StorageDead.
|
||||
kind: DropKind,
|
||||
|
||||
/// Whether this is a backwards-incompatible drop lint
|
||||
backwards_incompatible_lint: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) enum DropKind {
|
||||
Value,
|
||||
Storage,
|
||||
ForLint,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -248,7 +246,7 @@ impl Scope {
|
|||
/// use of optimizations in the MIR coroutine transform.
|
||||
fn needs_cleanup(&self) -> bool {
|
||||
self.drops.iter().any(|drop| match drop.kind {
|
||||
DropKind::Value => true,
|
||||
DropKind::Value | DropKind::ForLint => true,
|
||||
DropKind::Storage => false,
|
||||
})
|
||||
}
|
||||
|
|
@ -277,12 +275,8 @@ impl DropTree {
|
|||
// represents the block in the tree that should be jumped to once all
|
||||
// of the required drops have been performed.
|
||||
let fake_source_info = SourceInfo::outermost(DUMMY_SP);
|
||||
let fake_data = DropData {
|
||||
source_info: fake_source_info,
|
||||
local: Local::MAX,
|
||||
kind: DropKind::Storage,
|
||||
backwards_incompatible_lint: false,
|
||||
};
|
||||
let fake_data =
|
||||
DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage };
|
||||
let drops = IndexVec::from_raw(vec![DropNode { data: fake_data, next: DropIdx::MAX }]);
|
||||
Self { drops, entry_points: Vec::new(), existing_drops_map: FxHashMap::default() }
|
||||
}
|
||||
|
|
@ -411,6 +405,27 @@ impl DropTree {
|
|||
};
|
||||
cfg.terminate(block, drop_node.data.source_info, terminator);
|
||||
}
|
||||
DropKind::ForLint => {
|
||||
let stmt = Statement {
|
||||
source_info: drop_node.data.source_info,
|
||||
kind: StatementKind::BackwardIncompatibleDropHint {
|
||||
place: Box::new(drop_node.data.local.into()),
|
||||
reason: BackwardIncompatibleDropReason::Edition2024,
|
||||
},
|
||||
};
|
||||
cfg.push(block, stmt);
|
||||
let target = blocks[drop_node.next].unwrap();
|
||||
if target != block {
|
||||
// Diagnostics don't use this `Span` but debuginfo
|
||||
// might. Since we don't want breakpoints to be placed
|
||||
// here, especially when this is on an unwind path, we
|
||||
// use `DUMMY_SP`.
|
||||
let source_info =
|
||||
SourceInfo { span: DUMMY_SP, ..drop_node.data.source_info };
|
||||
let terminator = TerminatorKind::Goto { target };
|
||||
cfg.terminate(block, source_info, terminator);
|
||||
}
|
||||
}
|
||||
// Root nodes don't correspond to a drop.
|
||||
DropKind::Storage if drop_idx == ROOT_NODE => {}
|
||||
DropKind::Storage => {
|
||||
|
|
@ -770,12 +785,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let local =
|
||||
place.as_local().unwrap_or_else(|| bug!("projection in tail call args"));
|
||||
|
||||
Some(DropData {
|
||||
source_info,
|
||||
local,
|
||||
kind: DropKind::Value,
|
||||
backwards_incompatible_lint: false,
|
||||
})
|
||||
Some(DropData { source_info, local, kind: DropKind::Value })
|
||||
}
|
||||
Operand::Constant(_) => None,
|
||||
})
|
||||
|
|
@ -822,6 +832,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
});
|
||||
block = next;
|
||||
}
|
||||
DropKind::ForLint => {
|
||||
self.cfg.push(block, Statement {
|
||||
source_info,
|
||||
kind: StatementKind::BackwardIncompatibleDropHint {
|
||||
place: Box::new(local.into()),
|
||||
reason: BackwardIncompatibleDropReason::Edition2024,
|
||||
},
|
||||
});
|
||||
}
|
||||
DropKind::Storage => {
|
||||
// Only temps and vars need their storage dead.
|
||||
assert!(local.index() > self.arg_count);
|
||||
|
|
@ -1021,7 +1040,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
drop_kind: DropKind,
|
||||
) {
|
||||
let needs_drop = match drop_kind {
|
||||
DropKind::Value => {
|
||||
DropKind::Value | DropKind::ForLint => {
|
||||
if !self.local_decls[local].ty.needs_drop(self.tcx, self.typing_env()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1101,7 +1120,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
source_info: SourceInfo { span: scope_end, scope: scope.source_scope },
|
||||
local,
|
||||
kind: drop_kind,
|
||||
backwards_incompatible_lint: false,
|
||||
});
|
||||
|
||||
return;
|
||||
|
|
@ -1119,10 +1137,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
region_scope: region::Scope,
|
||||
local: Local,
|
||||
) {
|
||||
if !self.local_decls[local].ty.has_significant_drop(self.tcx, ty::TypingEnv {
|
||||
typing_mode: ty::TypingMode::non_body_analysis(),
|
||||
param_env: self.param_env,
|
||||
}) {
|
||||
if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) {
|
||||
return;
|
||||
}
|
||||
for scope in self.scopes.scopes.iter_mut().rev() {
|
||||
|
|
@ -1135,8 +1150,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
scope.drops.push(DropData {
|
||||
source_info: SourceInfo { span: scope_end, scope: scope.source_scope },
|
||||
local,
|
||||
kind: DropKind::Value,
|
||||
backwards_incompatible_lint: true,
|
||||
kind: DropKind::ForLint,
|
||||
});
|
||||
|
||||
return;
|
||||
|
|
@ -1379,12 +1393,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Builds drops for `pop_scope` and `leave_top_scope`.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `unwind_drops`, the drop tree data structure storing what needs to be cleaned up if unwind occurs
|
||||
/// * `scope`, describes the drops that will occur on exiting the scope in regular execution
|
||||
/// * `block`, the block to branch to once drops are complete (assuming no unwind occurs)
|
||||
/// * `unwind_to`, describes the drops that would occur at this point in the code if a
|
||||
/// panic occurred (a subset of the drops in `scope`, since we sometimes elide StorageDead and other
|
||||
/// instructions on unwinding)
|
||||
/// * `storage_dead_on_unwind`, if true, then we should emit `StorageDead` even when unwinding
|
||||
/// * `arg_count`, number of MIR local variables corresponding to fn arguments (used to assert that we don't drop those)
|
||||
fn build_scope_drops<'tcx>(
|
||||
cfg: &mut CFG<'tcx>,
|
||||
unwind_drops: &mut DropTree,
|
||||
scope: &Scope,
|
||||
mut block: BasicBlock,
|
||||
mut unwind_to: DropIdx,
|
||||
block: BasicBlock,
|
||||
unwind_to: DropIdx,
|
||||
storage_dead_on_unwind: bool,
|
||||
arg_count: usize,
|
||||
) -> BlockAnd<()> {
|
||||
|
|
@ -1409,6 +1434,18 @@ fn build_scope_drops<'tcx>(
|
|||
// drops for the unwind path should have already been generated by
|
||||
// `diverge_cleanup_gen`.
|
||||
|
||||
// `unwind_to` indicates what needs to be dropped should unwinding occur.
|
||||
// This is a subset of what needs to be dropped when exiting the scope.
|
||||
// As we unwind the scope, we will also move `unwind_to` backwards to match,
|
||||
// so that we can use it should a destructor panic.
|
||||
let mut unwind_to = unwind_to;
|
||||
|
||||
// The block that we should jump to after drops complete. We start by building the final drop (`drops[n]`
|
||||
// in the diagram above) and then build the drops (e.g., `drop[1]`, `drop[0]`) that come before it.
|
||||
// block begins as the successor of `drops[n]` and then becomes `drops[n]` so that `drops[n-1]`
|
||||
// will branch to `drops[n]`.
|
||||
let mut block = block;
|
||||
|
||||
for drop_data in scope.drops.iter().rev() {
|
||||
let source_info = drop_data.source_info;
|
||||
let local = drop_data.local;
|
||||
|
|
@ -1418,6 +1455,9 @@ fn build_scope_drops<'tcx>(
|
|||
// `unwind_to` should drop the value that we're about to
|
||||
// schedule. If dropping this value panics, then we continue
|
||||
// with the *next* value on the unwind path.
|
||||
//
|
||||
// We adjust this BEFORE we create the drop (e.g., `drops[n]`)
|
||||
// because `drops[n]` should unwind to `drops[n-1]`.
|
||||
debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local);
|
||||
debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind);
|
||||
unwind_to = unwind_drops.drops[unwind_to].next;
|
||||
|
|
@ -1430,27 +1470,50 @@ fn build_scope_drops<'tcx>(
|
|||
continue;
|
||||
}
|
||||
|
||||
if drop_data.backwards_incompatible_lint {
|
||||
cfg.push(block, Statement {
|
||||
source_info,
|
||||
kind: StatementKind::BackwardIncompatibleDropHint {
|
||||
place: Box::new(local.into()),
|
||||
reason: BackwardIncompatibleDropReason::Edition2024,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
unwind_drops.add_entry_point(block, unwind_to);
|
||||
let next = cfg.start_new_block();
|
||||
cfg.terminate(block, source_info, TerminatorKind::Drop {
|
||||
place: local.into(),
|
||||
target: next,
|
||||
unwind: UnwindAction::Continue,
|
||||
replace: false,
|
||||
});
|
||||
block = next;
|
||||
unwind_drops.add_entry_point(block, unwind_to);
|
||||
let next = cfg.start_new_block();
|
||||
cfg.terminate(block, source_info, TerminatorKind::Drop {
|
||||
place: local.into(),
|
||||
target: next,
|
||||
unwind: UnwindAction::Continue,
|
||||
replace: false,
|
||||
});
|
||||
block = next;
|
||||
}
|
||||
DropKind::ForLint => {
|
||||
// If the operand has been moved, and we are not on an unwind
|
||||
// path, then don't generate the drop. (We only take this into
|
||||
// account for non-unwind paths so as not to disturb the
|
||||
// caching mechanism.)
|
||||
if scope.moved_locals.iter().any(|&o| o == local) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// As in the `DropKind::Storage` case below:
|
||||
// normally lint-related drops are not emitted for unwind,
|
||||
// so we can just leave `unwind_to` unmodified, but in some
|
||||
// cases we emit things ALSO on the unwind path, so we need to adjust
|
||||
// `unwind_to` in that case.
|
||||
if storage_dead_on_unwind {
|
||||
debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local);
|
||||
debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind);
|
||||
unwind_to = unwind_drops.drops[unwind_to].next;
|
||||
}
|
||||
|
||||
cfg.push(block, Statement {
|
||||
source_info,
|
||||
kind: StatementKind::BackwardIncompatibleDropHint {
|
||||
place: Box::new(local.into()),
|
||||
reason: BackwardIncompatibleDropReason::Edition2024,
|
||||
},
|
||||
});
|
||||
}
|
||||
DropKind::Storage => {
|
||||
// Ordinarily, storage-dead nodes are not emitted on unwind, so we don't
|
||||
// need to adjust `unwind_to` on this path. However, in some specific cases
|
||||
// we *do* emit storage-dead nodes on the unwind path, and in that case now that
|
||||
// the storage-dead has completed, we need to adjust the `unwind_to` pointer
|
||||
// so that any future drops we emit will not register storage-dead.
|
||||
if storage_dead_on_unwind {
|
||||
debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local);
|
||||
debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind);
|
||||
|
|
@ -1500,7 +1563,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
|||
unwind_indices.push(unwind_indices[drop_node.next]);
|
||||
}
|
||||
}
|
||||
DropKind::Value => {
|
||||
DropKind::Value | DropKind::ForLint => {
|
||||
let unwind_drop = self
|
||||
.scopes
|
||||
.unwind_drops
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{
|
||||
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
|
||||
MultiSpan, SubdiagMessageOp, Subdiagnostic,
|
||||
MultiSpan, SubdiagMessageOp, Subdiagnostic, pluralize,
|
||||
};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
|
@ -1088,18 +1088,20 @@ pub(crate) enum RustcBoxAttrReason {
|
|||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_build_rust_2024_incompatible_pat)]
|
||||
pub(crate) struct Rust2024IncompatiblePat {
|
||||
pub(crate) struct Rust2024IncompatiblePat<'a> {
|
||||
#[subdiagnostic]
|
||||
pub(crate) sugg: Rust2024IncompatiblePatSugg,
|
||||
pub(crate) sugg: Rust2024IncompatiblePatSugg<'a>,
|
||||
}
|
||||
|
||||
pub(crate) struct Rust2024IncompatiblePatSugg {
|
||||
pub(crate) struct Rust2024IncompatiblePatSugg<'a> {
|
||||
pub(crate) suggestion: Vec<(Span, String)>,
|
||||
/// Whether the incompatibility is a hard error because a relevant span is in edition 2024.
|
||||
pub(crate) is_hard_error: bool,
|
||||
pub(crate) ref_pattern_count: usize,
|
||||
pub(crate) binding_mode_count: usize,
|
||||
/// Labeled spans for subpatterns invalid in Rust 2024.
|
||||
pub(crate) labels: &'a [(Span, String)],
|
||||
}
|
||||
|
||||
impl Subdiagnostic for Rust2024IncompatiblePatSugg {
|
||||
impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
|
|
@ -1111,6 +1113,16 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
|
|||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
diag.multipart_suggestion("desugar the match ergonomics", self.suggestion, applicability);
|
||||
let plural_derefs = pluralize!(self.ref_pattern_count);
|
||||
let and_modes = if self.binding_mode_count > 0 {
|
||||
format!(" and variable binding mode{}", pluralize!(self.binding_mode_count))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("make the implied reference pattern{plural_derefs}{and_modes} explicit"),
|
||||
self.suggestion,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ impl<'tcx> Cx<'tcx> {
|
|||
let block = Block {
|
||||
targeted_by_break: block.targeted_by_break,
|
||||
region_scope: region::Scope {
|
||||
id: block.hir_id.local_id,
|
||||
local_id: block.hir_id.local_id,
|
||||
data: region::ScopeData::Node,
|
||||
},
|
||||
span: block.span,
|
||||
|
|
@ -51,7 +51,7 @@ impl<'tcx> Cx<'tcx> {
|
|||
let stmt = Stmt {
|
||||
kind: StmtKind::Expr {
|
||||
scope: region::Scope {
|
||||
id: hir_id.local_id,
|
||||
local_id: hir_id.local_id,
|
||||
data: region::ScopeData::Node,
|
||||
},
|
||||
expr: self.mirror_expr(expr),
|
||||
|
|
@ -65,7 +65,7 @@ impl<'tcx> Cx<'tcx> {
|
|||
}
|
||||
hir::StmtKind::Let(local) => {
|
||||
let remainder_scope = region::Scope {
|
||||
id: block_id,
|
||||
local_id: block_id,
|
||||
data: region::ScopeData::Remainder(region::FirstStatementIndex::new(
|
||||
index,
|
||||
)),
|
||||
|
|
@ -108,7 +108,7 @@ impl<'tcx> Cx<'tcx> {
|
|||
kind: StmtKind::Let {
|
||||
remainder_scope,
|
||||
init_scope: region::Scope {
|
||||
id: hir_id.local_id,
|
||||
local_id: hir_id.local_id,
|
||||
data: region::ScopeData::Node,
|
||||
},
|
||||
pattern,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ impl<'tcx> Cx<'tcx> {
|
|||
#[instrument(level = "trace", skip(self, hir_expr))]
|
||||
pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId {
|
||||
let expr_scope =
|
||||
region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
|
||||
region::Scope { local_id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
|
||||
|
||||
trace!(?hir_expr.hir_id, ?hir_expr.span);
|
||||
|
||||
|
|
@ -814,14 +814,20 @@ impl<'tcx> Cx<'tcx> {
|
|||
hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) },
|
||||
hir::ExprKind::Break(dest, ref value) => match dest.target_id {
|
||||
Ok(target_id) => ExprKind::Break {
|
||||
label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node },
|
||||
label: region::Scope {
|
||||
local_id: target_id.local_id,
|
||||
data: region::ScopeData::Node,
|
||||
},
|
||||
value: value.map(|value| self.mirror_expr(value)),
|
||||
},
|
||||
Err(err) => bug!("invalid loop id for break: {}", err),
|
||||
},
|
||||
hir::ExprKind::Continue(dest) => match dest.target_id {
|
||||
Ok(loop_id) => ExprKind::Continue {
|
||||
label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node },
|
||||
label: region::Scope {
|
||||
local_id: loop_id.local_id,
|
||||
data: region::ScopeData::Node,
|
||||
},
|
||||
},
|
||||
Err(err) => bug!("invalid loop id for continue: {}", err),
|
||||
},
|
||||
|
|
@ -831,7 +837,7 @@ impl<'tcx> Cx<'tcx> {
|
|||
},
|
||||
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
|
||||
if_then_scope: region::Scope {
|
||||
id: then.hir_id.local_id,
|
||||
local_id: then.hir_id.local_id,
|
||||
data: {
|
||||
if expr.span.at_least_rust_2024() {
|
||||
region::ScopeData::IfThenRescope
|
||||
|
|
@ -1021,7 +1027,7 @@ impl<'tcx> Cx<'tcx> {
|
|||
guard: arm.guard.as_ref().map(|g| self.mirror_expr(g)),
|
||||
body: self.mirror_expr(arm.body),
|
||||
lint_level: LintLevel::Explicit(arm.hir_id),
|
||||
scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node },
|
||||
scope: region::Scope { local_id: arm.hir_id.local_id, data: region::ScopeData::Node },
|
||||
span: arm.span,
|
||||
};
|
||||
self.thir.arms.push(arm)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ mod const_to_pat;
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use rustc_abi::{FieldIdx, Integer};
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
||||
|
|
@ -34,7 +35,7 @@ struct PatCtxt<'a, 'tcx> {
|
|||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
|
||||
/// Used by the Rust 2024 migration lint.
|
||||
rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg>,
|
||||
rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg<'a>>,
|
||||
}
|
||||
|
||||
pub(super) fn pat_from_hir<'a, 'tcx>(
|
||||
|
|
@ -50,24 +51,36 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
|
|||
rust_2024_migration_suggestion: typeck_results
|
||||
.rust_2024_migration_desugared_pats()
|
||||
.get(pat.hir_id)
|
||||
.map(|&is_hard_error| Rust2024IncompatiblePatSugg {
|
||||
.map(|labels| Rust2024IncompatiblePatSugg {
|
||||
suggestion: Vec::new(),
|
||||
is_hard_error,
|
||||
ref_pattern_count: 0,
|
||||
binding_mode_count: 0,
|
||||
labels: labels.as_slice(),
|
||||
}),
|
||||
};
|
||||
let result = pcx.lower_pattern(pat);
|
||||
debug!("pat_from_hir({:?}) = {:?}", pat, result);
|
||||
if let Some(sugg) = pcx.rust_2024_migration_suggestion {
|
||||
if sugg.is_hard_error {
|
||||
let mut spans = MultiSpan::from_spans(sugg.labels.iter().map(|(span, _)| *span).collect());
|
||||
for (span, label) in sugg.labels {
|
||||
spans.push_span_label(*span, label.clone());
|
||||
}
|
||||
// If a relevant span is from at least edition 2024, this is a hard error.
|
||||
let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
|
||||
if is_hard_error {
|
||||
let mut err =
|
||||
tcx.dcx().struct_span_err(pat.span, fluent::mir_build_rust_2024_incompatible_pat);
|
||||
tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
|
||||
if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
|
||||
// provide the same reference link as the lint
|
||||
err.note(format!("for more information, see {}", info.reference));
|
||||
}
|
||||
err.subdiagnostic(sugg);
|
||||
err.emit();
|
||||
} else {
|
||||
tcx.emit_node_span_lint(
|
||||
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
|
||||
pat.hir_id,
|
||||
pat.span,
|
||||
spans,
|
||||
Rust2024IncompatiblePat { sugg },
|
||||
);
|
||||
}
|
||||
|
|
@ -133,6 +146,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
})
|
||||
.collect();
|
||||
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
|
||||
s.ref_pattern_count += adjustments.len();
|
||||
};
|
||||
|
||||
adjusted_pat
|
||||
|
|
@ -371,7 +385,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
s.suggestion.push((
|
||||
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
|
||||
sugg_str.to_owned(),
|
||||
))
|
||||
));
|
||||
s.binding_mode_count += 1;
|
||||
}
|
||||
|
||||
// A ref x pattern is the same node used for x, and as such it has
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
use std::ops::RangeInclusive;
|
||||
|
||||
use rustc_middle::mir::{
|
||||
self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdges,
|
||||
};
|
||||
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
|
||||
|
||||
use super::visitor::ResultsVisitor;
|
||||
use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget};
|
||||
|
|
@ -78,8 +76,6 @@ impl Direction for Backward {
|
|||
for pred in body.basic_blocks.predecessors()[block].iter().copied() {
|
||||
match body[pred].terminator().kind {
|
||||
// Apply terminator-specific edge effects.
|
||||
//
|
||||
// FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
|
||||
mir::TerminatorKind::Call { destination, target: Some(dest), .. }
|
||||
if dest == block =>
|
||||
{
|
||||
|
|
@ -115,18 +111,18 @@ impl Direction for Backward {
|
|||
}
|
||||
|
||||
mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
|
||||
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
|
||||
body,
|
||||
pred,
|
||||
exit_state,
|
||||
block,
|
||||
propagate: &mut propagate,
|
||||
effects_applied: false,
|
||||
};
|
||||
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
|
||||
let values = &body.basic_blocks.switch_sources()[&(block, pred)];
|
||||
let targets =
|
||||
values.iter().map(|&value| SwitchIntTarget { value, target: block });
|
||||
|
||||
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
|
||||
|
||||
if !applier.effects_applied {
|
||||
let mut tmp = analysis.bottom_value(body);
|
||||
for target in targets {
|
||||
tmp.clone_from(&exit_state);
|
||||
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, target);
|
||||
propagate(pred, &tmp);
|
||||
}
|
||||
} else {
|
||||
propagate(pred, exit_state)
|
||||
}
|
||||
}
|
||||
|
|
@ -245,37 +241,6 @@ impl Direction for Backward {
|
|||
}
|
||||
}
|
||||
|
||||
struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> {
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
pred: BasicBlock,
|
||||
exit_state: &'mir mut D,
|
||||
block: BasicBlock,
|
||||
propagate: &'mir mut F,
|
||||
effects_applied: bool,
|
||||
}
|
||||
|
||||
impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, D, F>
|
||||
where
|
||||
D: Clone,
|
||||
F: FnMut(BasicBlock, &D),
|
||||
{
|
||||
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
|
||||
assert!(!self.effects_applied);
|
||||
|
||||
let values = &self.body.basic_blocks.switch_sources()[&(self.block, self.pred)];
|
||||
let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.block });
|
||||
|
||||
let mut tmp = None;
|
||||
for target in targets {
|
||||
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
|
||||
apply_edge_effect(tmp, target);
|
||||
(self.propagate)(self.pred, tmp);
|
||||
}
|
||||
|
||||
self.effects_applied = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
|
||||
pub struct Forward;
|
||||
|
||||
|
|
@ -284,7 +249,7 @@ impl Direction for Forward {
|
|||
|
||||
fn apply_effects_in_block<'mir, 'tcx, A>(
|
||||
analysis: &mut A,
|
||||
_body: &mir::Body<'tcx>,
|
||||
body: &mir::Body<'tcx>,
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||
|
|
@ -324,23 +289,28 @@ impl Direction for Forward {
|
|||
}
|
||||
}
|
||||
TerminatorEdges::SwitchInt { targets, discr } => {
|
||||
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
|
||||
exit_state,
|
||||
targets,
|
||||
propagate,
|
||||
effects_applied: false,
|
||||
};
|
||||
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
|
||||
let mut tmp = analysis.bottom_value(body);
|
||||
for (value, target) in targets.iter() {
|
||||
tmp.clone_from(&exit_state);
|
||||
analysis.apply_switch_int_edge_effect(
|
||||
&mut data,
|
||||
&mut tmp,
|
||||
SwitchIntTarget { value: Some(value), target },
|
||||
);
|
||||
propagate(target, &tmp);
|
||||
}
|
||||
|
||||
analysis.apply_switch_int_edge_effects(block, discr, &mut applier);
|
||||
|
||||
let ForwardSwitchIntEdgeEffectsApplier {
|
||||
exit_state,
|
||||
mut propagate,
|
||||
effects_applied,
|
||||
..
|
||||
} = applier;
|
||||
|
||||
if !effects_applied {
|
||||
// Once we get to the final, "otherwise" branch, there is no need to preserve
|
||||
// `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
|
||||
// a clone of the dataflow state.
|
||||
let otherwise = targets.otherwise();
|
||||
analysis.apply_switch_int_edge_effect(&mut data, exit_state, SwitchIntTarget {
|
||||
value: None,
|
||||
target: otherwise,
|
||||
});
|
||||
propagate(otherwise, exit_state);
|
||||
} else {
|
||||
for target in targets.all_targets() {
|
||||
propagate(*target, exit_state);
|
||||
}
|
||||
|
|
@ -454,54 +424,3 @@ impl Direction for Forward {
|
|||
vis.visit_block_end(state);
|
||||
}
|
||||
}
|
||||
|
||||
struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> {
|
||||
exit_state: &'mir mut D,
|
||||
targets: &'mir SwitchTargets,
|
||||
propagate: F,
|
||||
|
||||
effects_applied: bool,
|
||||
}
|
||||
|
||||
impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
|
||||
where
|
||||
D: Clone,
|
||||
F: FnMut(BasicBlock, &D),
|
||||
{
|
||||
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
|
||||
assert!(!self.effects_applied);
|
||||
|
||||
let mut tmp = None;
|
||||
for (value, target) in self.targets.iter() {
|
||||
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
|
||||
apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
|
||||
(self.propagate)(target, tmp);
|
||||
}
|
||||
|
||||
// Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
|
||||
// so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
|
||||
let otherwise = self.targets.otherwise();
|
||||
apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
|
||||
(self.propagate)(otherwise, self.exit_state);
|
||||
|
||||
self.effects_applied = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses
|
||||
/// the more efficient `clone_from` if `opt` was `Some`.
|
||||
///
|
||||
/// Returns a mutable reference to the new clone that resides in `opt`.
|
||||
//
|
||||
// FIXME: Figure out how to express this using `Option::clone_from`, or maybe lift it into the
|
||||
// standard library?
|
||||
fn opt_clone_from_or_clone<'a, T: Clone>(opt: &'a mut Option<T>, val: &T) -> &'a mut T {
|
||||
if opt.is_some() {
|
||||
let ret = opt.as_mut().unwrap();
|
||||
ret.clone_from(val);
|
||||
ret
|
||||
} else {
|
||||
*opt = Some(val.clone());
|
||||
opt.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,9 @@ pub trait Analysis<'tcx> {
|
|||
/// The direction of this analysis. Either `Forward` or `Backward`.
|
||||
type Direction: Direction = Forward;
|
||||
|
||||
/// Auxiliary data used for analyzing `SwitchInt` terminators, if necessary.
|
||||
type SwitchIntData = !;
|
||||
|
||||
/// A descriptive name for this analysis. Used only for debugging.
|
||||
///
|
||||
/// This name should be brief and contain no spaces, periods or other characters that are not
|
||||
|
|
@ -190,25 +193,36 @@ pub trait Analysis<'tcx> {
|
|||
) {
|
||||
}
|
||||
|
||||
/// Updates the current dataflow state with the effect of taking a particular branch in a
|
||||
/// `SwitchInt` terminator.
|
||||
/// Used to update the current dataflow state with the effect of taking a particular branch in
|
||||
/// a `SwitchInt` terminator.
|
||||
///
|
||||
/// Unlike the other edge-specific effects, which are allowed to mutate `Self::Domain`
|
||||
/// directly, overriders of this method must pass a callback to
|
||||
/// `SwitchIntEdgeEffects::apply`. The callback will be run once for each outgoing edge and
|
||||
/// will have access to the dataflow state that will be propagated along that edge.
|
||||
/// directly, overriders of this method must return a `Self::SwitchIntData` value (wrapped in
|
||||
/// `Some`). The `apply_switch_int_edge_effect` method will then be called once for each
|
||||
/// outgoing edge and will have access to the dataflow state that will be propagated along that
|
||||
/// edge, and also the `Self::SwitchIntData` value.
|
||||
///
|
||||
/// This interface is somewhat more complex than the other visitor-like "effect" methods.
|
||||
/// However, it is both more ergonomic—callers don't need to recompute or cache information
|
||||
/// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the
|
||||
/// engine doesn't need to clone the exit state for a block unless
|
||||
/// `SwitchIntEdgeEffects::apply` is actually called.
|
||||
fn apply_switch_int_edge_effects(
|
||||
/// `get_switch_int_data` is actually called.
|
||||
fn get_switch_int_data(
|
||||
&mut self,
|
||||
_block: BasicBlock,
|
||||
_block: mir::BasicBlock,
|
||||
_discr: &mir::Operand<'tcx>,
|
||||
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
|
||||
) -> Option<Self::SwitchIntData> {
|
||||
None
|
||||
}
|
||||
|
||||
/// See comments on `get_switch_int_data`.
|
||||
fn apply_switch_int_edge_effect(
|
||||
&mut self,
|
||||
_data: &mut Self::SwitchIntData,
|
||||
_state: &mut Self::Domain,
|
||||
_edge: SwitchIntTarget,
|
||||
) {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
/* Extension methods */
|
||||
|
|
@ -421,12 +435,5 @@ pub struct SwitchIntTarget {
|
|||
pub target: BasicBlock,
|
||||
}
|
||||
|
||||
/// A type that records the edge-specific effects for a `SwitchInt` terminator.
|
||||
pub trait SwitchIntEdgeEffects<D> {
|
||||
/// Calls `apply_edge_effect` for each outgoing edge from a `SwitchInt` terminator and
|
||||
/// records the results.
|
||||
fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ where
|
|||
{
|
||||
fn visit_block_start(&mut self, _state: &A::Domain) {}
|
||||
|
||||
/// // njn: grep for "before", "primary", etc.
|
||||
/// Called after the "early" effect of the given statement is applied to `state`.
|
||||
fn visit_after_early_statement_effect(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,96 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
|
||||
use rustc_abi::VariantIdx;
|
||||
use rustc_index::Idx;
|
||||
use rustc_index::bit_set::{BitSet, MixedBitSet};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdges};
|
||||
use rustc_middle::ty::util::Discr;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::elaborate_drops::DropFlagState;
|
||||
use crate::framework::SwitchIntEdgeEffects;
|
||||
use crate::framework::SwitchIntTarget;
|
||||
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
|
||||
use crate::{
|
||||
Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry,
|
||||
drop_flag_effects_for_location, on_all_children_bits, on_lookup_result_bits,
|
||||
};
|
||||
|
||||
// Used by both `MaybeInitializedPlaces` and `MaybeUninitializedPlaces`.
|
||||
pub struct MaybePlacesSwitchIntData<'tcx> {
|
||||
enum_place: mir::Place<'tcx>,
|
||||
discriminants: Vec<(VariantIdx, Discr<'tcx>)>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'tcx> MaybePlacesSwitchIntData<'tcx> {
|
||||
// The discriminant order in the `SwitchInt` targets should match the order yielded by
|
||||
// `AdtDef::discriminants`. We rely on this to match each discriminant in the targets to its
|
||||
// corresponding variant in linear time.
|
||||
fn next_discr(&mut self, value: u128) -> VariantIdx {
|
||||
// An out-of-bounds abort will occur if the discriminant ordering isn't as described above.
|
||||
loop {
|
||||
let (variant, discr) = self.discriminants[self.index];
|
||||
self.index += 1;
|
||||
if discr.val == value {
|
||||
return variant;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MaybePlacesSwitchIntData<'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
block: mir::BasicBlock,
|
||||
discr: &mir::Operand<'tcx>,
|
||||
) -> Option<Self> {
|
||||
let Some(discr) = discr.place() else { return None };
|
||||
|
||||
// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt`
|
||||
// is an enum discriminant.
|
||||
//
|
||||
// We expect such blocks to have a call to `discriminant` as their last statement like so:
|
||||
// ```text
|
||||
// ...
|
||||
// _42 = discriminant(_1)
|
||||
// SwitchInt(_42, ..)
|
||||
// ```
|
||||
// If the basic block matches this pattern, this function gathers the place corresponding
|
||||
// to the enum (`_1` in the example above) as well as the discriminants.
|
||||
let block_data = &body[block];
|
||||
for statement in block_data.statements.iter().rev() {
|
||||
match statement.kind {
|
||||
mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(enum_place)))
|
||||
if lhs == discr =>
|
||||
{
|
||||
match enum_place.ty(body, tcx).ty.kind() {
|
||||
ty::Adt(enum_def, _) => {
|
||||
return Some(MaybePlacesSwitchIntData {
|
||||
enum_place,
|
||||
discriminants: enum_def.discriminants(tcx).collect(),
|
||||
index: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// `Rvalue::Discriminant` is also used to get the active yield point for a
|
||||
// coroutine, but we do not need edge-specific effects in that case. This
|
||||
// may change in the future.
|
||||
ty::Coroutine(..) => break,
|
||||
|
||||
t => bug!("`discriminant` called on unexpected type {:?}", t),
|
||||
}
|
||||
}
|
||||
mir::StatementKind::Coverage(_) => continue,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// `MaybeInitializedPlaces` tracks all places that might be
|
||||
/// initialized upon reaching a particular point in the control flow
|
||||
/// for a function.
|
||||
|
|
@ -247,6 +323,8 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
|||
/// We use a mixed bitset to avoid paying too high a memory footprint.
|
||||
type Domain = MaybeReachable<MixedBitSet<MovePathIndex>>;
|
||||
|
||||
type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
|
||||
|
||||
const NAME: &'static str = "maybe_init";
|
||||
|
||||
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
|
||||
|
|
@ -293,6 +371,8 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
|||
terminator: &'mir mir::Terminator<'tcx>,
|
||||
location: Location,
|
||||
) -> TerminatorEdges<'mir, 'tcx> {
|
||||
// Note: `edges` must be computed first because `drop_flag_effects_for_location` can change
|
||||
// the result of `is_unwind_dead`.
|
||||
let mut edges = terminator.edges();
|
||||
if self.skip_unreachable_unwind
|
||||
&& let mir::TerminatorKind::Drop { target, unwind, place, replace: _ } = terminator.kind
|
||||
|
|
@ -326,46 +406,34 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
|||
});
|
||||
}
|
||||
|
||||
fn apply_switch_int_edge_effects(
|
||||
fn get_switch_int_data(
|
||||
&mut self,
|
||||
block: mir::BasicBlock,
|
||||
discr: &mir::Operand<'tcx>,
|
||||
edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
|
||||
) {
|
||||
) -> Option<Self::SwitchIntData> {
|
||||
if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let enum_ = discr.place().and_then(|discr| {
|
||||
switch_on_enum_discriminant(self.tcx, self.body, &self.body[block], discr)
|
||||
});
|
||||
|
||||
let Some((enum_place, enum_def)) = enum_ else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut discriminants = enum_def.discriminants(self.tcx);
|
||||
edge_effects.apply(|state, edge| {
|
||||
let Some(value) = edge.value else {
|
||||
return;
|
||||
};
|
||||
|
||||
// MIR building adds discriminants to the `values` array in the same order as they
|
||||
// are yielded by `AdtDef::discriminants`. We rely on this to match each
|
||||
// discriminant in `values` to its corresponding variant in linear time.
|
||||
let (variant, _) = discriminants
|
||||
.find(|&(_, discr)| discr.val == value)
|
||||
.expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`");
|
||||
MaybePlacesSwitchIntData::new(self.tcx, self.body, block, discr)
|
||||
}
|
||||
|
||||
fn apply_switch_int_edge_effect(
|
||||
&mut self,
|
||||
data: &mut Self::SwitchIntData,
|
||||
state: &mut Self::Domain,
|
||||
edge: SwitchIntTarget,
|
||||
) {
|
||||
if let Some(value) = edge.value {
|
||||
// Kill all move paths that correspond to variants we know to be inactive along this
|
||||
// particular outgoing edge of a `SwitchInt`.
|
||||
drop_flag_effects::on_all_inactive_variants(
|
||||
self.move_data(),
|
||||
enum_place,
|
||||
variant,
|
||||
self.move_data,
|
||||
data.enum_place,
|
||||
data.next_discr(value),
|
||||
|mpi| state.kill(mpi),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -376,6 +444,8 @@ pub type MaybeUninitializedPlacesDomain = MixedBitSet<MovePathIndex>;
|
|||
impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
||||
type Domain = MaybeUninitializedPlacesDomain;
|
||||
|
||||
type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
|
||||
|
||||
const NAME: &'static str = "maybe_uninit";
|
||||
|
||||
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
|
||||
|
|
@ -445,50 +515,38 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
|||
});
|
||||
}
|
||||
|
||||
fn apply_switch_int_edge_effects(
|
||||
fn get_switch_int_data(
|
||||
&mut self,
|
||||
block: mir::BasicBlock,
|
||||
discr: &mir::Operand<'tcx>,
|
||||
edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
|
||||
) {
|
||||
) -> Option<Self::SwitchIntData> {
|
||||
if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
if !self.mark_inactive_variants_as_uninit {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let enum_ = discr.place().and_then(|discr| {
|
||||
switch_on_enum_discriminant(self.tcx, self.body, &self.body[block], discr)
|
||||
});
|
||||
|
||||
let Some((enum_place, enum_def)) = enum_ else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut discriminants = enum_def.discriminants(self.tcx);
|
||||
edge_effects.apply(|state, edge| {
|
||||
let Some(value) = edge.value else {
|
||||
return;
|
||||
};
|
||||
|
||||
// MIR building adds discriminants to the `values` array in the same order as they
|
||||
// are yielded by `AdtDef::discriminants`. We rely on this to match each
|
||||
// discriminant in `values` to its corresponding variant in linear time.
|
||||
let (variant, _) = discriminants
|
||||
.find(|&(_, discr)| discr.val == value)
|
||||
.expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`");
|
||||
MaybePlacesSwitchIntData::new(self.tcx, self.body, block, discr)
|
||||
}
|
||||
|
||||
fn apply_switch_int_edge_effect(
|
||||
&mut self,
|
||||
data: &mut Self::SwitchIntData,
|
||||
state: &mut Self::Domain,
|
||||
edge: SwitchIntTarget,
|
||||
) {
|
||||
if let Some(value) = edge.value {
|
||||
// Mark all move paths that correspond to variants other than this one as maybe
|
||||
// uninitialized (in reality, they are *definitely* uninitialized).
|
||||
drop_flag_effects::on_all_inactive_variants(
|
||||
self.move_data(),
|
||||
enum_place,
|
||||
variant,
|
||||
self.move_data,
|
||||
data.enum_place,
|
||||
data.next_discr(value),
|
||||
|mpi| state.gen_(mpi),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -578,45 +636,3 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is
|
||||
/// an enum discriminant.
|
||||
///
|
||||
/// We expect such blocks to have a call to `discriminant` as their last statement like so:
|
||||
///
|
||||
/// ```text
|
||||
/// ...
|
||||
/// _42 = discriminant(_1)
|
||||
/// SwitchInt(_42, ..)
|
||||
/// ```
|
||||
///
|
||||
/// If the basic block matches this pattern, this function returns the place corresponding to the
|
||||
/// enum (`_1` in the example above) as well as the `AdtDef` of that enum.
|
||||
fn switch_on_enum_discriminant<'mir, 'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
block: &'mir mir::BasicBlockData<'tcx>,
|
||||
switch_on: mir::Place<'tcx>,
|
||||
) -> Option<(mir::Place<'tcx>, ty::AdtDef<'tcx>)> {
|
||||
for statement in block.statements.iter().rev() {
|
||||
match &statement.kind {
|
||||
mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))
|
||||
if *lhs == switch_on =>
|
||||
{
|
||||
match discriminated.ty(body, tcx).ty.kind() {
|
||||
ty::Adt(def, _) => return Some((*discriminated, *def)),
|
||||
|
||||
// `Rvalue::Discriminant` is also used to get the active yield point for a
|
||||
// coroutine, but we do not need edge-specific effects in that case. This may
|
||||
// change in the future.
|
||||
ty::Coroutine(..) => return None,
|
||||
|
||||
t => bug!("`discriminant` called on unexpected type {:?}", t),
|
||||
}
|
||||
}
|
||||
mir::StatementKind::Coverage(_) => continue,
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
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