Merge from rust-lang/rust
This commit is contained in:
commit
3c4a690d01
888 changed files with 34234 additions and 19450 deletions
54
.github/ISSUE_TEMPLATE/rustdoc.md
vendored
Normal file
54
.github/ISSUE_TEMPLATE/rustdoc.md
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
name: Problem with rustdoc
|
||||
about: Report an issue with how docs get generated.
|
||||
labels: C-bug, T-rustdoc
|
||||
---
|
||||
<!--
|
||||
Thank you for filing a rustdoc issue! Rustdoc is the tool that handles the generation of docs. It is usually invoked via `cargo doc`, but can also be used directly.
|
||||
|
||||
If you have an issue with the actual content of the docs, use the "Documentation problem" template instead.
|
||||
-->
|
||||
|
||||
# Code
|
||||
<!-- problematic snippet and/or link to repo and/or full path of standard library function -->
|
||||
|
||||
```rust
|
||||
<code>
|
||||
```
|
||||
|
||||
# Reproduction Steps
|
||||
<!--
|
||||
* command(s) to run, if any
|
||||
* permalink to hosted documentation, if any
|
||||
* search query, if any
|
||||
-->
|
||||
|
||||
# Expected Outcome
|
||||
<!--
|
||||
What did you want to happen?
|
||||
|
||||
For GUI issues, feel free to provide a mockup image of what you want it to look like.
|
||||
|
||||
For diagnostics, please provide a mockup of the desired output in a code block.
|
||||
-->
|
||||
|
||||
# Actual Output
|
||||
<!--
|
||||
* rustdoc console output
|
||||
* browser screenshot of generated html
|
||||
* rustdoc json (prettify by running through `jq` or running thorugh an online formatter)
|
||||
-->
|
||||
```console
|
||||
<code>
|
||||
```
|
||||
|
||||
|
||||
# Version
|
||||
<!--
|
||||
Available via `rustdoc --version` or under the "Help" menu.
|
||||
|
||||
If the issue involves opening the documentation in a browser, please also provide the name and version of the browser used.
|
||||
-->
|
||||
|
||||
# Additional Details
|
||||
<!-- Anything else you think is relevant -->
|
||||
1
.mailmap
1
.mailmap
|
|
@ -698,3 +698,4 @@ Zach Pomerantz <zmp@umich.edu>
|
|||
Zack Corr <zack@z0w0.me> <zackcorr95@gmail.com>
|
||||
Zack Slayton <zack.slayton@gmail.com>
|
||||
Zbigniew Siciarz <zbigniew@siciarz.net> Zbigniew Siciarz <antyqjon@gmail.com>
|
||||
y21 <30553356+y21@users.noreply.github.com>
|
||||
|
|
|
|||
281
Cargo.lock
281
Cargo.lock
|
|
@ -13,9 +13,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
|
|
@ -75,7 +75,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"unicode-width 0.2.0",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -136,7 +136,7 @@ dependencies = [
|
|||
"anstyle-lossy",
|
||||
"anstyle-parse",
|
||||
"html-escape",
|
||||
"unicode-width 0.2.0",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -204,7 +204,7 @@ dependencies = [
|
|||
"rustc-hash 2.1.1",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -216,7 +216,7 @@ dependencies = [
|
|||
"memchr",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"winnow 0.7.10",
|
||||
"winnow 0.7.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -431,9 +431,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
|
|
@ -487,9 +487,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.39"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f"
|
||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
|
@ -507,9 +507,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.39"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51"
|
||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -519,21 +519,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.32"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
|
||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
|
|
@ -558,7 +558,7 @@ dependencies = [
|
|||
"rustc_tools_util 0.4.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"tempfile",
|
||||
"termize",
|
||||
"tokio",
|
||||
|
|
@ -673,7 +673,7 @@ dependencies = [
|
|||
"nom",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -748,7 +748,7 @@ dependencies = [
|
|||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width 0.2.0",
|
||||
"unicode-width 0.2.1",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
|
|
@ -900,7 +900,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -911,7 +911,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
|||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -939,7 +939,7 @@ checksum = "e73f2692d4bd3cac41dca28934a39894200c9fabf49586d77d0e5954af1d7902"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -960,7 +960,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -970,7 +970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -982,7 +982,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1050,7 +1050,7 @@ dependencies = [
|
|||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.5.0",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1072,7 +1072,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1352,7 +1352,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1416,11 +1416,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.14",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1431,7 +1431,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1511,9 +1511,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
|
|
@ -1775,7 +1775,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1859,7 +1859,7 @@ dependencies = [
|
|||
"console",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width 0.2.0",
|
||||
"unicode-width 0.2.1",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
|
|
@ -1933,9 +1933,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93"
|
||||
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
|
|
@ -1946,13 +1946,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442"
|
||||
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2045,9 +2045,9 @@ checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libdbus-sys"
|
||||
|
|
@ -2085,7 +2085,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.53.0",
|
||||
"windows-targets 0.53.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2223,7 +2223,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2261,9 +2261,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
|
|
@ -2276,9 +2276,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "minifier"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bfdc64e2f805f3d12965f10522000bae36e88d2cfea44112331f467d4f4bf68"
|
||||
checksum = "14f1541610994bba178cb36757e102d06a52a2d9612aa6d34c64b3b377c5d943"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
|
|
@ -2288,9 +2288,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
|
@ -2512,15 +2512,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.37.0"
|
||||
version = "0.37.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6273adb7096cf9ab4335f258e627d8230e69d40d45567d678f552dcec6245215"
|
||||
checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"hashbrown",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
"wasmparser 0.232.0",
|
||||
"wasmparser 0.234.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2725,7 +2725,7 @@ dependencies = [
|
|||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2983,6 +2983,15 @@ dependencies = [
|
|||
"getrandom 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||
dependencies = [
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xoshiro"
|
||||
version = "0.7.0"
|
||||
|
|
@ -3014,9 +3023,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.12"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
|
@ -3148,9 +3157,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
|
|
@ -3182,16 +3191,6 @@ dependencies = [
|
|||
"tikv-jemalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-rayon-core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f42932dcd3bcbe484b38a3ccf79b7906fac41c02d408b5b1bac26da3416efdb"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-semver"
|
||||
version = "1.1.0"
|
||||
|
|
@ -3440,7 +3439,7 @@ dependencies = [
|
|||
"itertools",
|
||||
"libc",
|
||||
"measureme",
|
||||
"object 0.37.0",
|
||||
"object 0.37.1",
|
||||
"rustc-demangle",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
|
|
@ -3474,14 +3473,12 @@ name = "rustc_codegen_ssa"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"ar_archive_writer",
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"bstr",
|
||||
"cc",
|
||||
"either",
|
||||
"itertools",
|
||||
"libc",
|
||||
"object 0.37.0",
|
||||
"object 0.37.1",
|
||||
"pathdiff",
|
||||
"regex",
|
||||
"rustc_abi",
|
||||
|
|
@ -3560,7 +3557,6 @@ dependencies = [
|
|||
"parking_lot",
|
||||
"portable-atomic",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustc-rayon-core",
|
||||
"rustc-stable-hash",
|
||||
"rustc_arena",
|
||||
"rustc_graphviz",
|
||||
|
|
@ -3568,6 +3564,7 @@ dependencies = [
|
|||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_thread_pool",
|
||||
"smallvec",
|
||||
"stacker",
|
||||
"tempfile",
|
||||
|
|
@ -3741,7 +3738,7 @@ dependencies = [
|
|||
"fluent-syntax",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
|
|
@ -3890,7 +3887,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3915,7 +3912,6 @@ dependencies = [
|
|||
name = "rustc_interface"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc-rayon-core",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_lowering",
|
||||
|
|
@ -3954,6 +3950,7 @@ dependencies = [
|
|||
"rustc_span",
|
||||
"rustc_symbol_mangling",
|
||||
"rustc_target",
|
||||
"rustc_thread_pool",
|
||||
"rustc_trait_selection",
|
||||
"rustc_traits",
|
||||
"rustc_ty_utils",
|
||||
|
|
@ -4037,7 +4034,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -4081,7 +4078,6 @@ dependencies = [
|
|||
"either",
|
||||
"gsgdt",
|
||||
"polonius-engine",
|
||||
"rustc-rayon-core",
|
||||
"rustc_abi",
|
||||
"rustc_apfloat",
|
||||
"rustc_arena",
|
||||
|
|
@ -4105,6 +4101,7 @@ dependencies = [
|
|||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"rustc_thread_pool",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
|
|
@ -4242,7 +4239,7 @@ dependencies = [
|
|||
"thin-vec",
|
||||
"tracing",
|
||||
"unicode-normalization",
|
||||
"unicode-width 0.2.0",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4351,7 +4348,6 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"hashbrown",
|
||||
"parking_lot",
|
||||
"rustc-rayon-core",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_attr_data_structures",
|
||||
|
|
@ -4366,6 +4362,7 @@ dependencies = [
|
|||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_thread_pool",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
]
|
||||
|
|
@ -4491,7 +4488,7 @@ dependencies = [
|
|||
"sha1",
|
||||
"sha2",
|
||||
"tracing",
|
||||
"unicode-width 0.2.0",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4516,7 +4513,7 @@ name = "rustc_target"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"object 0.37.0",
|
||||
"object 0.37.1",
|
||||
"rustc_abi",
|
||||
"rustc_data_structures",
|
||||
"rustc_fs_util",
|
||||
|
|
@ -4527,6 +4524,18 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_thread_pool"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"libc",
|
||||
"rand 0.9.1",
|
||||
"rand_xorshift",
|
||||
"scoped-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_tools_util"
|
||||
version = "0.4.2"
|
||||
|
|
@ -4637,7 +4646,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -4728,7 +4737,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4856,7 +4865,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5096,9 +5105,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
version = "2.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -5113,7 +5122,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5246,7 +5255,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5257,7 +5266,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5274,12 +5283,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5433,7 +5441,7 @@ checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5604,7 +5612,7 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5"
|
|||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"unic-langid-impl",
|
||||
]
|
||||
|
||||
|
|
@ -5672,9 +5680,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
|
|
@ -5775,9 +5783,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
|
|
@ -5816,7 +5824,7 @@ dependencies = [
|
|||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
|
@ -5838,7 +5846,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
|
@ -5901,12 +5909,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.233.0"
|
||||
version = "0.235.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9679ae3cf7cfa2ca3a327f7fab97f27f3294d402fd1a76ca8ab514e17973e4d3"
|
||||
checksum = "b3bc393c395cb621367ff02d854179882b9a351b4e0c93d1397e6090b53a5c2a"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser 0.233.0",
|
||||
"wasmparser 0.235.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5946,18 +5954,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.232.0"
|
||||
version = "0.234.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "917739b33bb1eb0e9a49bcd2637a351931be4578d0cc4d37b908d7a797784fbb"
|
||||
checksum = "be22e5a8f600afce671dd53c8d2dd26b4b7aa810fd18ae27dfc49737f3e02fc5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.233.0"
|
||||
version = "0.235.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b51cb03afce7964bbfce46602d6cb358726f36430b6ba084ac6020d8ce5bc102"
|
||||
checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
|
|
@ -5966,22 +5974,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "233.0.0"
|
||||
version = "235.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eaf4099d8d0c922b83bf3c90663f5666f0769db9e525184284ebbbdb1dd2180"
|
||||
checksum = "1eda4293f626c99021bb3a6fbe4fbbe90c0e31a5ace89b5f620af8925de72e13"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"leb128fmt",
|
||||
"memchr",
|
||||
"unicode-width 0.2.0",
|
||||
"wasm-encoder 0.233.0",
|
||||
"unicode-width 0.2.1",
|
||||
"wasm-encoder 0.235.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.233.0"
|
||||
version = "1.235.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d9bc80f5e4b25ea086ef41b91ccd244adde45d931c384d94a8ff64ab8bd7d87"
|
||||
checksum = "e777e0327115793cb96ab220b98f85327ec3d11f34ec9e8d723264522ef206aa"
|
||||
dependencies = [
|
||||
"wast",
|
||||
]
|
||||
|
|
@ -6029,9 +6037,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.1"
|
||||
version = "0.61.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
|
||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
|
|
@ -6092,7 +6100,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6103,14 +6111,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
|
|
@ -6167,6 +6175,15 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
|
|
@ -6200,9 +6217,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.0"
|
||||
version = "0.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
|
|
@ -6372,9 +6389,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.10"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
||||
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
@ -6507,7 +6524,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6519,7 +6536,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6540,7 +6557,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6560,7 +6577,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6605,7 +6622,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6616,5 +6633,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.103",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ exclude = [
|
|||
"obj",
|
||||
]
|
||||
|
||||
[profile.release.package.rustc-rayon-core]
|
||||
[profile.release.package.rustc_thread_pool]
|
||||
# The rustc fork of Rayon has deadlock detection code which intermittently
|
||||
# causes overflows in the CI (see https://github.com/rust-lang/rust/issues/90227)
|
||||
# so we turn overflow checks off for now.
|
||||
|
|
|
|||
100
RELEASES.md
100
RELEASES.md
|
|
@ -1,3 +1,103 @@
|
|||
Version 1.88.0 (2025-06-26)
|
||||
==========================
|
||||
|
||||
<a id="1.88.0-Language"></a>
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Stabilize `#![feature(let_chains)]` in the 2024 edition.](https://github.com/rust-lang/rust/pull/132833)
|
||||
This feature allows `&&`-chaining `let` statements inside `if` and `while`, allowing intermixture with boolean expressions. The patterns inside the `let` sub-expressions can be irrefutable or refutable.
|
||||
- [Stabilize `#![feature(naked_functions)]`.](https://github.com/rust-lang/rust/pull/134213)
|
||||
Naked functions allow writing functions with no compiler-generated epilogue and prologue, allowing full control over the generated assembly for a particular function.
|
||||
- [Stabilize `#![feature(cfg_boolean_literals)]`.](https://github.com/rust-lang/rust/pull/138632)
|
||||
This allows using boolean literals as `cfg` predicates, e.g. `#[cfg(true)]` and `#[cfg(false)]`.
|
||||
- [Fully de-stabilize the `#[bench]` attribute](https://github.com/rust-lang/rust/pull/134273). Usage of `#[bench]` without `#![feature(custom_test_frameworks)]` already triggered a deny-by-default future-incompatibility lint since Rust 1.77, but will now become a hard error.
|
||||
- [Add warn-by-default `dangerous_implicit_autorefs` lint against implicit autoref of raw pointer dereference.](https://github.com/rust-lang/rust/pull/123239)
|
||||
The lint [will be bumped to deny-by-default](https://github.com/rust-lang/rust/pull/141661) in the next version of Rust.
|
||||
- [Add `invalid_null_arguments` lint to prevent invalid usage of null pointers.](https://github.com/rust-lang/rust/pull/119220)
|
||||
This lint is uplifted from `clippy::invalid_null_ptr_usage`.
|
||||
- [Change trait impl candidate preference for builtin impls and trivial where-clauses.](https://github.com/rust-lang/rust/pull/138176)
|
||||
- [Check types of generic const parameter defaults](https://github.com/rust-lang/rust/pull/139646)
|
||||
|
||||
<a id="1.88.0-Compiler"></a>
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [Stabilize `-Cdwarf-version` for selecting the version of DWARF debug information to generate.](https://github.com/rust-lang/rust/pull/136926)
|
||||
|
||||
|
||||
<a id="1.88.0-Platform-Support"></a>
|
||||
|
||||
Platform Support
|
||||
----------------
|
||||
- [Demote `i686-pc-windows-gnu` to Tier 2.](https://blog.rust-lang.org/2025/05/26/demoting-i686-pc-windows-gnu/)
|
||||
|
||||
|
||||
Refer to Rust's [platform support page][platform-support-doc]
|
||||
for more information on Rust's tiered platform support.
|
||||
|
||||
[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html
|
||||
|
||||
<a id="1.88.0-Libraries"></a>
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [Remove backticks from `#[should_panic]` test failure message.](https://github.com/rust-lang/rust/pull/136160)
|
||||
- [Guarantee that `[T; N]::from_fn` is generated in order of increasing indices.](https://github.com/rust-lang/rust/pull/139099), for those passing it a stateful closure.
|
||||
- [The libtest flag `--nocapture` is deprecated in favor of the more consistent `--no-capture` flag.](https://github.com/rust-lang/rust/pull/139224)
|
||||
- [Guarantee that `{float}::NAN` is a quiet NaN.](https://github.com/rust-lang/rust/pull/139483)
|
||||
|
||||
|
||||
<a id="1.88.0-Stabilized-APIs"></a>
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
|
||||
- [`Cell::update`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#method.update)
|
||||
- [`impl Default for *const T`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#impl-Default-for-*const+T)
|
||||
- [`impl Default for *mut T`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#impl-Default-for-*mut+T)
|
||||
- [`HashMap::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html#method.extract_if)
|
||||
- [`HashSet::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.HashSet.html#method.extract_if)
|
||||
- [`proc_macro::Span::line`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.line)
|
||||
- [`proc_macro::Span::column`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.column)
|
||||
- [`proc_macro::Span::start`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.start)
|
||||
- [`proc_macro::Span::end`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.end)
|
||||
- [`proc_macro::Span::file`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.file)
|
||||
- [`proc_macro::Span::local_file`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.local_file)
|
||||
|
||||
These previously stable APIs are now stable in const contexts:
|
||||
|
||||
- [`NonNull<T>::replace`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.replace)
|
||||
- [`<*mut T>::replace`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.replace)
|
||||
- [`std::ptr::swap_nonoverlapping`](https://github.com/rust-lang/rust/pull/137280)
|
||||
- [`Cell::{replace, get, get_mut, from_mut, as_slice_of_cells}`](https://github.com/rust-lang/rust/pull/137928)
|
||||
|
||||
|
||||
<a id="1.88.0-Cargo"></a>
|
||||
|
||||
Cargo
|
||||
-----
|
||||
- [Stabilize automatic garbage collection.](https://github.com/rust-lang/cargo/pull/14287/)
|
||||
- [use `zlib-rs` for gzip compression in rust code](https://github.com/rust-lang/cargo/pull/15417/)
|
||||
|
||||
<a id="1.88.0-Rustdoc"></a>
|
||||
|
||||
Rustdoc
|
||||
-----
|
||||
- [Doctests can be ignored based on target names using `ignore-*` attributes.](https://github.com/rust-lang/rust/pull/137096)
|
||||
- [Stabilize the `--test-runtool` and `--test-runtool-arg` CLI options to specify a program (like qemu) and its arguments to run a doctest.](https://github.com/rust-lang/rust/pull/137096)
|
||||
|
||||
<a id="1.88.0-Compatibility-Notes"></a>
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- [Finish changing the internal representation of pasted tokens](https://github.com/rust-lang/rust/pull/124141). Certain invalid declarative macros that were previously accepted in obscure circumstances are now correctly rejected by the compiler. Use of a `tt` fragment specifier can often fix these macros.
|
||||
- [Fully de-stabilize the `#[bench]` attribute](https://github.com/rust-lang/rust/pull/134273). Usage of `#[bench]` without `#![feature(custom_test_frameworks)]` already triggered a deny-by-default future-incompatibility lint since Rust 1.77, but will now become a hard error.
|
||||
- [Fix borrow checking some always-true patterns.](https://github.com/rust-lang/rust/pull/139042)
|
||||
The borrow checker was overly permissive in some cases, allowing programs that shouldn't have compiled.
|
||||
- [Update the minimum external LLVM to 19.](https://github.com/rust-lang/rust/pull/139275)
|
||||
- [Make it a hard error to use a vector type with a non-Rust ABI without enabling the required target feature.](https://github.com/rust-lang/rust/pull/139309)
|
||||
|
||||
Version 1.87.0 (2025-05-15)
|
||||
==========================
|
||||
|
||||
|
|
|
|||
|
|
@ -432,7 +432,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
align = align.min(AbiAlign::new(pack));
|
||||
}
|
||||
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
|
||||
// See documentation on `LayoutS::unadjusted_abi_align`.
|
||||
// See documentation on `LayoutData::unadjusted_abi_align`.
|
||||
let unadjusted_abi_align = align.abi;
|
||||
if let Some(repr_align) = repr.align {
|
||||
align = align.max(AbiAlign::new(repr_align));
|
||||
|
|
@ -602,10 +602,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
dont_niche_optimize_enum: bool,
|
||||
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
|
||||
// Until we've decided whether to use the tagged or
|
||||
// niche filling LayoutS, we don't want to intern the
|
||||
// niche filling LayoutData, we don't want to intern the
|
||||
// variant layouts, so we can't store them in the
|
||||
// overall LayoutS. Store the overall LayoutS
|
||||
// and the variant LayoutSs here until then.
|
||||
// overall LayoutData. Store the overall LayoutData
|
||||
// and the variant LayoutDatas here until then.
|
||||
struct TmpLayout<FieldIdx: Idx, VariantIdx: Idx> {
|
||||
layout: LayoutData<FieldIdx, VariantIdx>,
|
||||
variants: IndexVec<VariantIdx, LayoutData<FieldIdx, VariantIdx>>,
|
||||
|
|
@ -1214,7 +1214,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
|
||||
match kind {
|
||||
StructKind::AlwaysSized | StructKind::MaybeUnsized => {
|
||||
// Currently `LayoutS` only exposes a single niche so sorting is usually
|
||||
// Currently `LayoutData` only exposes a single niche so sorting is usually
|
||||
// sufficient to get one niche into the preferred position. If it ever
|
||||
// supported multiple niches then a more advanced pick-and-pack approach could
|
||||
// provide better results. But even for the single-niche cache it's not
|
||||
|
|
@ -1333,7 +1333,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
}
|
||||
|
||||
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
|
||||
// See documentation on `LayoutS::unadjusted_abi_align`.
|
||||
// See documentation on `LayoutData::unadjusted_abi_align`.
|
||||
let unadjusted_abi_align = align.abi;
|
||||
if let Some(repr_align) = repr.align {
|
||||
align = align.max(AbiAlign::new(repr_align));
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ pub struct Layout<'a>(pub Interned<'a, LayoutData<FieldIdx, VariantIdx>>);
|
|||
|
||||
impl<'a> fmt::Debug for Layout<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// See comment on `<LayoutS as Debug>::fmt` above.
|
||||
// See comment on `<LayoutData as Debug>::fmt` above.
|
||||
self.0.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1785,7 +1785,7 @@ where
|
|||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// This is how `Layout` used to print before it become
|
||||
// `Interned<LayoutS>`. We print it like this to avoid having to update
|
||||
// `Interned<LayoutData>`. We print it like this to avoid having to update
|
||||
// expected output in a lot of tests.
|
||||
let LayoutData {
|
||||
size,
|
||||
|
|
|
|||
|
|
@ -321,6 +321,13 @@ impl<Wrapped, Tag> AstNodeWrapper<Wrapped, Tag> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: remove after `stmt_expr_attributes` is stabilized.
|
||||
impl<T, Tag> From<AstNodeWrapper<P<T>, Tag>> for AstNodeWrapper<T, Tag> {
|
||||
fn from(value: AstNodeWrapper<P<T>, Tag>) -> Self {
|
||||
AstNodeWrapper { wrapped: *value.wrapped, tag: value.tag }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Wrapped: HasNodeId, Tag> HasNodeId for AstNodeWrapper<Wrapped, Tag> {
|
||||
fn node_id(&self) -> NodeId {
|
||||
self.wrapped.node_id()
|
||||
|
|
|
|||
|
|
@ -206,12 +206,24 @@ impl AttributeExt for Attribute {
|
|||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> AttrStyle {
|
||||
self.style
|
||||
fn doc_resolution_scope(&self) -> Option<AttrStyle> {
|
||||
match &self.kind {
|
||||
AttrKind::DocComment(..) => Some(self.style),
|
||||
AttrKind::Normal(normal)
|
||||
if normal.item.path == sym::doc && normal.item.value_str().is_some() =>
|
||||
{
|
||||
Some(self.style)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
pub fn style(&self) -> AttrStyle {
|
||||
self.style
|
||||
}
|
||||
|
||||
pub fn may_have_doc_links(&self) -> bool {
|
||||
self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
|
||||
}
|
||||
|
|
@ -806,7 +818,14 @@ pub trait AttributeExt: Debug {
|
|||
/// * `#[doc(...)]` returns `None`.
|
||||
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
|
||||
|
||||
fn style(&self) -> AttrStyle;
|
||||
/// Returns outer or inner if this is a doc attribute or a sugared doc
|
||||
/// comment, otherwise None.
|
||||
///
|
||||
/// This is used in the case of doc comments on modules, to decide whether
|
||||
/// to resolve intra-doc links against the symbols in scope within the
|
||||
/// commented module (for inner doc) vs within its parent module (for outer
|
||||
/// doc).
|
||||
fn doc_resolution_scope(&self) -> Option<AttrStyle>;
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): use function delegation instead of manually forwarding
|
||||
|
|
@ -881,8 +900,4 @@ impl Attribute {
|
|||
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
AttributeExt::doc_str_and_comment_kind(self)
|
||||
}
|
||||
|
||||
pub fn style(&self) -> AttrStyle {
|
||||
AttributeExt::style(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'st
|
|||
}
|
||||
}
|
||||
|
||||
pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable";
|
||||
pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2";
|
||||
|
||||
pub enum AllocatorTy {
|
||||
Layout,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,14 @@ pub struct FormatArgs {
|
|||
///
|
||||
/// Generally only useful for lints that care about the raw bytes the user wrote.
|
||||
pub uncooked_fmt_str: (LitKind, Symbol),
|
||||
/// Was the format literal written in the source?
|
||||
/// - `format!("boo")` => true,
|
||||
/// - `format!(concat!("b", "o", "o"))` => false,
|
||||
/// - `format!(include_str!("boo.txt"))` => false,
|
||||
///
|
||||
/// If it wasn't written in the source then we have to be careful with spans pointing into it
|
||||
/// and suggestions about rewriting it.
|
||||
pub is_source_literal: bool,
|
||||
}
|
||||
|
||||
/// A piece of a format template string.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use std::panic;
|
|||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{Ident, Span};
|
||||
use smallvec::{Array, SmallVec, smallvec};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::ast::*;
|
||||
|
|
@ -21,17 +21,6 @@ use crate::ptr::P;
|
|||
use crate::tokenstream::*;
|
||||
use crate::visit::{AssocCtxt, BoundKind, FnCtxt, VisitorResult, try_visit, visit_opt, walk_list};
|
||||
|
||||
pub trait ExpectOne<A: Array> {
|
||||
fn expect_one(self, err: &'static str) -> A::Item;
|
||||
}
|
||||
|
||||
impl<A: Array> ExpectOne<A> for SmallVec<A> {
|
||||
fn expect_one(self, err: &'static str) -> A::Item {
|
||||
assert!(self.len() == 1, "{}", err);
|
||||
self.into_iter().next().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
use rustc_ast_ir::visit::VisitorResult;
|
||||
|
||||
|
|
@ -47,323 +36,6 @@ mod sealed {
|
|||
|
||||
use sealed::MutVisitorResult;
|
||||
|
||||
pub trait MutVisitor: Sized + MutVisitorResult<Result = ()> {
|
||||
// Methods in this trait have one of three forms:
|
||||
//
|
||||
// fn visit_t(&mut self, t: &mut T); // common
|
||||
// fn flat_map_t(&mut self, t: T) -> SmallVec<[T; 1]>; // rare
|
||||
// fn filter_map_t(&mut self, t: T) -> Option<T>; // rarest
|
||||
//
|
||||
// When writing these methods, it is better to use destructuring like this:
|
||||
//
|
||||
// fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
|
||||
// visit_a(a);
|
||||
// visit_b(b);
|
||||
// }
|
||||
//
|
||||
// than to use field access like this:
|
||||
//
|
||||
// fn visit_abc(&mut self, abc: &mut ABC) {
|
||||
// visit_a(&mut abc.a);
|
||||
// visit_b(&mut abc.b);
|
||||
// // ignore abc.c
|
||||
// }
|
||||
//
|
||||
// As well as being more concise, the former is explicit about which fields
|
||||
// are skipped. Furthermore, if a new field is added, the destructuring
|
||||
// version will cause a compile error, which is good. In comparison, the
|
||||
// field access version will continue working and it would be easy to
|
||||
// forget to add handling for it.
|
||||
|
||||
fn visit_crate(&mut self, c: &mut Crate) {
|
||||
walk_crate(self, c)
|
||||
}
|
||||
|
||||
fn visit_meta_list_item(&mut self, list_item: &mut MetaItemInner) {
|
||||
walk_meta_list_item(self, list_item);
|
||||
}
|
||||
|
||||
fn visit_meta_item(&mut self, meta_item: &mut MetaItem) {
|
||||
walk_meta_item(self, meta_item);
|
||||
}
|
||||
|
||||
fn visit_use_tree(&mut self, use_tree: &mut UseTree) {
|
||||
walk_use_tree(self, use_tree);
|
||||
}
|
||||
|
||||
fn visit_foreign_item(&mut self, ni: &mut ForeignItem) {
|
||||
walk_item(self, ni);
|
||||
}
|
||||
|
||||
fn flat_map_foreign_item(&mut self, ni: P<ForeignItem>) -> SmallVec<[P<ForeignItem>; 1]> {
|
||||
walk_flat_map_foreign_item(self, ni)
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, i: &mut Item) {
|
||||
walk_item(self, i);
|
||||
}
|
||||
|
||||
fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
|
||||
walk_flat_map_item(self, i)
|
||||
}
|
||||
|
||||
fn visit_fn_header(&mut self, header: &mut FnHeader) {
|
||||
walk_fn_header(self, header);
|
||||
}
|
||||
|
||||
fn visit_field_def(&mut self, fd: &mut FieldDef) {
|
||||
walk_field_def(self, fd);
|
||||
}
|
||||
|
||||
fn flat_map_field_def(&mut self, fd: FieldDef) -> SmallVec<[FieldDef; 1]> {
|
||||
walk_flat_map_field_def(self, fd)
|
||||
}
|
||||
|
||||
fn visit_assoc_item(&mut self, i: &mut AssocItem, ctxt: AssocCtxt) {
|
||||
walk_assoc_item(self, i, ctxt)
|
||||
}
|
||||
|
||||
fn flat_map_assoc_item(
|
||||
&mut self,
|
||||
i: P<AssocItem>,
|
||||
ctxt: AssocCtxt,
|
||||
) -> SmallVec<[P<AssocItem>; 1]> {
|
||||
walk_flat_map_assoc_item(self, i, ctxt)
|
||||
}
|
||||
|
||||
fn visit_contract(&mut self, c: &mut FnContract) {
|
||||
walk_contract(self, c);
|
||||
}
|
||||
|
||||
fn visit_fn_decl(&mut self, d: &mut FnDecl) {
|
||||
walk_fn_decl(self, d);
|
||||
}
|
||||
|
||||
/// `Span` and `NodeId` are mutated at the caller site.
|
||||
fn visit_fn(&mut self, fk: FnKind<'_>, _: Span, _: NodeId) {
|
||||
walk_fn(self, fk)
|
||||
}
|
||||
|
||||
fn visit_coroutine_kind(&mut self, a: &mut CoroutineKind) {
|
||||
walk_coroutine_kind(self, a);
|
||||
}
|
||||
|
||||
fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
|
||||
walk_closure_binder(self, b);
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, b: &mut Block) {
|
||||
walk_block(self, b);
|
||||
}
|
||||
|
||||
fn flat_map_stmt(&mut self, s: Stmt) -> SmallVec<[Stmt; 1]> {
|
||||
walk_flat_map_stmt(self, s)
|
||||
}
|
||||
|
||||
fn visit_arm(&mut self, arm: &mut Arm) {
|
||||
walk_arm(self, arm);
|
||||
}
|
||||
|
||||
fn flat_map_arm(&mut self, arm: Arm) -> SmallVec<[Arm; 1]> {
|
||||
walk_flat_map_arm(self, arm)
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, p: &mut Pat) {
|
||||
walk_pat(self, p);
|
||||
}
|
||||
|
||||
fn visit_anon_const(&mut self, c: &mut AnonConst) {
|
||||
walk_anon_const(self, c);
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, e: &mut Expr) {
|
||||
walk_expr(self, e);
|
||||
}
|
||||
|
||||
/// This method is a hack to workaround unstable of `stmt_expr_attributes`.
|
||||
/// It can be removed once that feature is stabilized.
|
||||
fn visit_method_receiver_expr(&mut self, ex: &mut P<Expr>) {
|
||||
self.visit_expr(ex)
|
||||
}
|
||||
|
||||
fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
|
||||
walk_filter_map_expr(self, e)
|
||||
}
|
||||
|
||||
fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
|
||||
walk_generic_arg(self, arg);
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, t: &mut Ty) {
|
||||
walk_ty(self, t);
|
||||
}
|
||||
|
||||
fn visit_ty_pat(&mut self, t: &mut TyPat) {
|
||||
walk_ty_pat(self, t);
|
||||
}
|
||||
|
||||
fn visit_lifetime(&mut self, l: &mut Lifetime) {
|
||||
walk_lifetime(self, l);
|
||||
}
|
||||
|
||||
fn visit_assoc_item_constraint(&mut self, c: &mut AssocItemConstraint) {
|
||||
walk_assoc_item_constraint(self, c);
|
||||
}
|
||||
|
||||
fn visit_foreign_mod(&mut self, nm: &mut ForeignMod) {
|
||||
walk_foreign_mod(self, nm);
|
||||
}
|
||||
|
||||
fn visit_variant(&mut self, v: &mut Variant) {
|
||||
walk_variant(self, v);
|
||||
}
|
||||
|
||||
fn flat_map_variant(&mut self, v: Variant) -> SmallVec<[Variant; 1]> {
|
||||
walk_flat_map_variant(self, v)
|
||||
}
|
||||
|
||||
fn visit_ident(&mut self, i: &mut Ident) {
|
||||
self.visit_span(&mut i.span);
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, p: &mut Path) {
|
||||
walk_path(self, p);
|
||||
}
|
||||
|
||||
fn visit_path_segment(&mut self, p: &mut PathSegment) {
|
||||
walk_path_segment(self, p)
|
||||
}
|
||||
|
||||
fn visit_qself(&mut self, qs: &mut Option<P<QSelf>>) {
|
||||
walk_qself(self, qs);
|
||||
}
|
||||
|
||||
fn visit_generic_args(&mut self, p: &mut GenericArgs) {
|
||||
walk_generic_args(self, p);
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &mut Local) {
|
||||
walk_local(self, l);
|
||||
}
|
||||
|
||||
fn visit_mac_call(&mut self, mac: &mut MacCall) {
|
||||
walk_mac(self, mac);
|
||||
}
|
||||
|
||||
fn visit_macro_def(&mut self, def: &mut MacroDef) {
|
||||
walk_macro_def(self, def);
|
||||
}
|
||||
|
||||
fn visit_label(&mut self, label: &mut Label) {
|
||||
walk_label(self, label);
|
||||
}
|
||||
|
||||
fn visit_attribute(&mut self, at: &mut Attribute) {
|
||||
walk_attribute(self, at);
|
||||
}
|
||||
|
||||
fn visit_param(&mut self, param: &mut Param) {
|
||||
walk_param(self, param);
|
||||
}
|
||||
|
||||
fn flat_map_param(&mut self, param: Param) -> SmallVec<[Param; 1]> {
|
||||
walk_flat_map_param(self, param)
|
||||
}
|
||||
|
||||
fn visit_generics(&mut self, generics: &mut Generics) {
|
||||
walk_generics(self, generics);
|
||||
}
|
||||
|
||||
fn visit_trait_ref(&mut self, tr: &mut TraitRef) {
|
||||
walk_trait_ref(self, tr);
|
||||
}
|
||||
|
||||
fn visit_poly_trait_ref(&mut self, p: &mut PolyTraitRef) {
|
||||
walk_poly_trait_ref(self, p);
|
||||
}
|
||||
|
||||
fn visit_variant_data(&mut self, vdata: &mut VariantData) {
|
||||
walk_variant_data(self, vdata);
|
||||
}
|
||||
|
||||
fn visit_generic_param(&mut self, param: &mut GenericParam) {
|
||||
walk_generic_param(self, param)
|
||||
}
|
||||
|
||||
fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> {
|
||||
walk_flat_map_generic_param(self, param)
|
||||
}
|
||||
|
||||
fn visit_param_bound(&mut self, tpb: &mut GenericBound, _ctxt: BoundKind) {
|
||||
walk_param_bound(self, tpb);
|
||||
}
|
||||
|
||||
fn visit_precise_capturing_arg(&mut self, arg: &mut PreciseCapturingArg) {
|
||||
walk_precise_capturing_arg(self, arg);
|
||||
}
|
||||
|
||||
fn visit_expr_field(&mut self, f: &mut ExprField) {
|
||||
walk_expr_field(self, f);
|
||||
}
|
||||
|
||||
fn flat_map_expr_field(&mut self, f: ExprField) -> SmallVec<[ExprField; 1]> {
|
||||
walk_flat_map_expr_field(self, f)
|
||||
}
|
||||
|
||||
fn flat_map_where_predicate(
|
||||
&mut self,
|
||||
where_predicate: WherePredicate,
|
||||
) -> SmallVec<[WherePredicate; 1]> {
|
||||
walk_flat_map_where_predicate(self, where_predicate)
|
||||
}
|
||||
|
||||
fn visit_where_predicate_kind(&mut self, kind: &mut WherePredicateKind) {
|
||||
walk_where_predicate_kind(self, kind)
|
||||
}
|
||||
|
||||
fn visit_vis(&mut self, vis: &mut Visibility) {
|
||||
walk_vis(self, vis);
|
||||
}
|
||||
|
||||
fn visit_id(&mut self, _id: &mut NodeId) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// Span visiting is no longer used, but we keep it for now,
|
||||
// in case it's needed for something like #127241.
|
||||
fn visit_span(&mut self, _sp: &mut Span) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
fn visit_pat_field(&mut self, fp: &mut PatField) {
|
||||
walk_pat_field(self, fp)
|
||||
}
|
||||
|
||||
fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> {
|
||||
walk_flat_map_pat_field(self, fp)
|
||||
}
|
||||
|
||||
fn visit_inline_asm(&mut self, asm: &mut InlineAsm) {
|
||||
walk_inline_asm(self, asm)
|
||||
}
|
||||
|
||||
fn visit_inline_asm_sym(&mut self, sym: &mut InlineAsmSym) {
|
||||
walk_inline_asm_sym(self, sym)
|
||||
}
|
||||
|
||||
fn visit_format_args(&mut self, fmt: &mut FormatArgs) {
|
||||
walk_format_args(self, fmt)
|
||||
}
|
||||
|
||||
fn visit_capture_by(&mut self, capture_by: &mut CaptureBy) {
|
||||
walk_capture_by(self, capture_by)
|
||||
}
|
||||
|
||||
fn visit_fn_ret_ty(&mut self, fn_ret_ty: &mut FnRetTy) {
|
||||
walk_fn_ret_ty(self, fn_ret_ty)
|
||||
}
|
||||
}
|
||||
|
||||
super::common_visitor_and_walkers!((mut) MutVisitor);
|
||||
|
||||
macro_rules! generate_flat_map_visitor_fns {
|
||||
|
|
@ -398,22 +70,6 @@ generate_flat_map_visitor_fns! {
|
|||
visit_arms, Arm, flat_map_arm;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_thin_vec<T, F>(elems: &mut ThinVec<T>, mut visit_elem: F)
|
||||
where
|
||||
F: FnMut(&mut T),
|
||||
{
|
||||
for elem in elems {
|
||||
visit_elem(elem);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_attrs<T: MutVisitor>(vis: &mut T, attrs: &mut AttrVec) {
|
||||
for attr in attrs.iter_mut() {
|
||||
vis.visit_attribute(attr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_pat_field<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut fp: PatField,
|
||||
|
|
@ -431,47 +87,26 @@ fn visit_nested_use_tree<V: MutVisitor>(
|
|||
vis.visit_use_tree(nested_tree);
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_arm<T: MutVisitor>(vis: &mut T, mut arm: Arm) -> SmallVec<[Arm; 1]> {
|
||||
vis.visit_arm(&mut arm);
|
||||
smallvec![arm]
|
||||
macro_rules! generate_walk_flat_map_fns {
|
||||
($($fn_name:ident($Ty:ty$(,$extra_name:ident: $ExtraTy:ty)*) => $visit_fn_name:ident;)+) => {$(
|
||||
pub fn $fn_name<V: MutVisitor>(vis: &mut V, mut value: $Ty$(,$extra_name: $ExtraTy)*) -> SmallVec<[$Ty; 1]> {
|
||||
vis.$visit_fn_name(&mut value$(,$extra_name)*);
|
||||
smallvec![value]
|
||||
}
|
||||
)+};
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_variant<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut variant: Variant,
|
||||
) -> SmallVec<[Variant; 1]> {
|
||||
vis.visit_variant(&mut variant);
|
||||
smallvec![variant]
|
||||
}
|
||||
|
||||
fn walk_meta_list_item<T: MutVisitor>(vis: &mut T, li: &mut MetaItemInner) {
|
||||
match li {
|
||||
MetaItemInner::MetaItem(mi) => vis.visit_meta_item(mi),
|
||||
MetaItemInner::Lit(_lit) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_meta_item<T: MutVisitor>(vis: &mut T, mi: &mut MetaItem) {
|
||||
let MetaItem { unsafety: _, path: _, kind, span } = mi;
|
||||
match kind {
|
||||
MetaItemKind::Word => {}
|
||||
MetaItemKind::List(mis) => visit_thin_vec(mis, |mi| vis.visit_meta_list_item(mi)),
|
||||
MetaItemKind::NameValue(_s) => {}
|
||||
}
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_param<T: MutVisitor>(vis: &mut T, mut param: Param) -> SmallVec<[Param; 1]> {
|
||||
vis.visit_param(&mut param);
|
||||
smallvec![param]
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_generic_param<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut param: GenericParam,
|
||||
) -> SmallVec<[GenericParam; 1]> {
|
||||
vis.visit_generic_param(&mut param);
|
||||
smallvec![param]
|
||||
generate_walk_flat_map_fns! {
|
||||
walk_flat_map_arm(Arm) => visit_arm;
|
||||
walk_flat_map_variant(Variant) => visit_variant;
|
||||
walk_flat_map_param(Param) => visit_param;
|
||||
walk_flat_map_generic_param(GenericParam) => visit_generic_param;
|
||||
walk_flat_map_where_predicate(WherePredicate) => visit_where_predicate;
|
||||
walk_flat_map_field_def(FieldDef) => visit_field_def;
|
||||
walk_flat_map_expr_field(ExprField) => visit_expr_field;
|
||||
walk_flat_map_item(P<Item>) => visit_item;
|
||||
walk_flat_map_foreign_item(P<ForeignItem>) => visit_foreign_item;
|
||||
walk_flat_map_assoc_item(P<AssocItem>, ctxt: AssocCtxt) => visit_assoc_item;
|
||||
}
|
||||
|
||||
fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWhereClauses) {
|
||||
|
|
@ -482,63 +117,6 @@ fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWh
|
|||
vis.visit_span(span_after);
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_where_predicate<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut pred: WherePredicate,
|
||||
) -> SmallVec<[WherePredicate; 1]> {
|
||||
walk_where_predicate(vis, &mut pred);
|
||||
smallvec![pred]
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_field_def<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut fd: FieldDef,
|
||||
) -> SmallVec<[FieldDef; 1]> {
|
||||
vis.visit_field_def(&mut fd);
|
||||
smallvec![fd]
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_expr_field<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut f: ExprField,
|
||||
) -> SmallVec<[ExprField; 1]> {
|
||||
vis.visit_expr_field(&mut f);
|
||||
smallvec![f]
|
||||
}
|
||||
|
||||
pub fn walk_item_kind<K: WalkItemKind>(
|
||||
kind: &mut K,
|
||||
span: Span,
|
||||
id: NodeId,
|
||||
visibility: &mut Visibility,
|
||||
ctxt: K::Ctxt,
|
||||
vis: &mut impl MutVisitor,
|
||||
) {
|
||||
kind.walk(span, id, visibility, ctxt, vis)
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_item(vis: &mut impl MutVisitor, mut item: P<Item>) -> SmallVec<[P<Item>; 1]> {
|
||||
vis.visit_item(&mut item);
|
||||
smallvec![item]
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_foreign_item(
|
||||
vis: &mut impl MutVisitor,
|
||||
mut item: P<ForeignItem>,
|
||||
) -> SmallVec<[P<ForeignItem>; 1]> {
|
||||
vis.visit_foreign_item(&mut item);
|
||||
smallvec![item]
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_assoc_item(
|
||||
vis: &mut impl MutVisitor,
|
||||
mut item: P<AssocItem>,
|
||||
ctxt: AssocCtxt,
|
||||
) -> SmallVec<[P<AssocItem>; 1]> {
|
||||
vis.visit_assoc_item(&mut item, ctxt);
|
||||
smallvec![item]
|
||||
}
|
||||
|
||||
pub fn walk_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Option<P<Expr>> {
|
||||
vis.visit_expr(&mut e);
|
||||
Some(e)
|
||||
|
|
@ -576,35 +154,11 @@ fn walk_flat_map_stmt_kind<T: MutVisitor>(vis: &mut T, kind: StmtKind) -> SmallV
|
|||
StmtKind::Empty => smallvec![StmtKind::Empty],
|
||||
StmtKind::MacCall(mut mac) => {
|
||||
let MacCallStmt { mac: mac_, style: _, attrs, tokens: _ } = mac.deref_mut();
|
||||
visit_attrs(vis, attrs);
|
||||
for attr in attrs {
|
||||
vis.visit_attribute(attr);
|
||||
}
|
||||
vis.visit_mac_call(mac_);
|
||||
smallvec![StmtKind::MacCall(mac)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_capture_by<T: MutVisitor>(vis: &mut T, capture_by: &mut CaptureBy) {
|
||||
match capture_by {
|
||||
CaptureBy::Ref => {}
|
||||
CaptureBy::Value { move_kw } => {
|
||||
vis.visit_span(move_kw);
|
||||
}
|
||||
CaptureBy::Use { use_kw } => {
|
||||
vis.visit_span(use_kw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FnKind<'a> {
|
||||
/// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
|
||||
Fn(FnCtxt, &'a mut Visibility, &'a mut Fn),
|
||||
|
||||
/// E.g., `|x, y| body`.
|
||||
Closure(
|
||||
&'a mut ClosureBinder,
|
||||
&'a mut Option<CoroutineKind>,
|
||||
&'a mut P<FnDecl>,
|
||||
&'a mut P<Expr>,
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,45 +65,6 @@ impl BoundKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum FnKind<'a> {
|
||||
/// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
|
||||
Fn(FnCtxt, &'a Visibility, &'a Fn),
|
||||
|
||||
/// E.g., `|x, y| body`.
|
||||
Closure(&'a ClosureBinder, &'a Option<CoroutineKind>, &'a FnDecl, &'a Expr),
|
||||
}
|
||||
|
||||
impl<'a> FnKind<'a> {
|
||||
pub fn header(&self) -> Option<&'a FnHeader> {
|
||||
match *self {
|
||||
FnKind::Fn(_, _, Fn { sig, .. }) => Some(&sig.header),
|
||||
FnKind::Closure(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ident(&self) -> Option<&Ident> {
|
||||
match self {
|
||||
FnKind::Fn(_, _, Fn { ident, .. }) => Some(ident),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decl(&self) -> &'a FnDecl {
|
||||
match self {
|
||||
FnKind::Fn(_, _, Fn { sig, .. }) => &sig.decl,
|
||||
FnKind::Closure(_, _, decl, _) => decl,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctxt(&self) -> Option<FnCtxt> {
|
||||
match self {
|
||||
FnKind::Fn(ctxt, ..) => Some(*ctxt),
|
||||
FnKind::Closure(..) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum LifetimeCtxt {
|
||||
/// Appears in a reference type.
|
||||
|
|
@ -114,206 +75,405 @@ pub enum LifetimeCtxt {
|
|||
GenericArg,
|
||||
}
|
||||
|
||||
/// Each method of the `Visitor` trait is a hook to be potentially
|
||||
/// overridden. Each method's default implementation recursively visits
|
||||
/// the substructure of the input via the corresponding `walk` method;
|
||||
/// e.g., the `visit_item` method by default calls `visit::walk_item`.
|
||||
///
|
||||
/// If you want to ensure that your code handles every variant
|
||||
/// explicitly, you need to override each method. (And you also need
|
||||
/// to monitor future changes to `Visitor` in case a new method with a
|
||||
/// new default implementation gets introduced.)
|
||||
///
|
||||
/// Every `walk_*` method uses deconstruction to access fields of structs and
|
||||
/// enums. This will result in a compile error if a field is added, which makes
|
||||
/// it more likely the appropriate visit call will be added for it.
|
||||
pub trait Visitor<'ast>: Sized {
|
||||
/// The result type of the `visit_*` methods. Can be either `()`,
|
||||
/// or `ControlFlow<T>`.
|
||||
type Result: VisitorResult = ();
|
||||
|
||||
fn visit_ident(&mut self, _ident: &'ast Ident) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
fn visit_foreign_mod(&mut self, nm: &'ast ForeignMod) -> Self::Result {
|
||||
walk_foreign_mod(self, nm)
|
||||
}
|
||||
fn visit_foreign_item(&mut self, i: &'ast ForeignItem) -> Self::Result {
|
||||
walk_item(self, i)
|
||||
}
|
||||
fn visit_item(&mut self, i: &'ast Item) -> Self::Result {
|
||||
walk_item(self, i)
|
||||
}
|
||||
fn visit_local(&mut self, l: &'ast Local) -> Self::Result {
|
||||
walk_local(self, l)
|
||||
}
|
||||
fn visit_block(&mut self, b: &'ast Block) -> Self::Result {
|
||||
walk_block(self, b)
|
||||
}
|
||||
fn visit_stmt(&mut self, s: &'ast Stmt) -> Self::Result {
|
||||
walk_stmt(self, s)
|
||||
}
|
||||
fn visit_param(&mut self, param: &'ast Param) -> Self::Result {
|
||||
walk_param(self, param)
|
||||
}
|
||||
fn visit_arm(&mut self, a: &'ast Arm) -> Self::Result {
|
||||
walk_arm(self, a)
|
||||
}
|
||||
fn visit_pat(&mut self, p: &'ast Pat) -> Self::Result {
|
||||
walk_pat(self, p)
|
||||
}
|
||||
fn visit_anon_const(&mut self, c: &'ast AnonConst) -> Self::Result {
|
||||
walk_anon_const(self, c)
|
||||
}
|
||||
fn visit_expr(&mut self, ex: &'ast Expr) -> Self::Result {
|
||||
walk_expr(self, ex)
|
||||
}
|
||||
/// This method is a hack to workaround unstable of `stmt_expr_attributes`.
|
||||
/// It can be removed once that feature is stabilized.
|
||||
fn visit_method_receiver_expr(&mut self, ex: &'ast Expr) -> Self::Result {
|
||||
self.visit_expr(ex)
|
||||
}
|
||||
fn visit_ty(&mut self, t: &'ast Ty) -> Self::Result {
|
||||
walk_ty(self, t)
|
||||
}
|
||||
fn visit_ty_pat(&mut self, t: &'ast TyPat) -> Self::Result {
|
||||
walk_ty_pat(self, t)
|
||||
}
|
||||
fn visit_generic_param(&mut self, param: &'ast GenericParam) -> Self::Result {
|
||||
walk_generic_param(self, param)
|
||||
}
|
||||
fn visit_generics(&mut self, g: &'ast Generics) -> Self::Result {
|
||||
walk_generics(self, g)
|
||||
}
|
||||
fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result {
|
||||
walk_closure_binder(self, b)
|
||||
}
|
||||
fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result {
|
||||
walk_contract(self, c)
|
||||
}
|
||||
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result {
|
||||
walk_where_predicate(self, p)
|
||||
}
|
||||
fn visit_where_predicate_kind(&mut self, k: &'ast WherePredicateKind) -> Self::Result {
|
||||
walk_where_predicate_kind(self, k)
|
||||
}
|
||||
fn visit_fn(&mut self, fk: FnKind<'ast>, _: Span, _: NodeId) -> Self::Result {
|
||||
walk_fn(self, fk)
|
||||
}
|
||||
fn visit_assoc_item(&mut self, i: &'ast AssocItem, ctxt: AssocCtxt) -> Self::Result {
|
||||
walk_assoc_item(self, i, ctxt)
|
||||
}
|
||||
fn visit_trait_ref(&mut self, t: &'ast TraitRef) -> Self::Result {
|
||||
walk_trait_ref(self, t)
|
||||
}
|
||||
fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) -> Self::Result {
|
||||
walk_param_bound(self, bounds)
|
||||
}
|
||||
fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) -> Self::Result {
|
||||
walk_precise_capturing_arg(self, arg)
|
||||
}
|
||||
fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef) -> Self::Result {
|
||||
walk_poly_trait_ref(self, t)
|
||||
}
|
||||
fn visit_variant_data(&mut self, s: &'ast VariantData) -> Self::Result {
|
||||
walk_variant_data(self, s)
|
||||
}
|
||||
fn visit_field_def(&mut self, s: &'ast FieldDef) -> Self::Result {
|
||||
walk_field_def(self, s)
|
||||
}
|
||||
fn visit_variant(&mut self, v: &'ast Variant) -> Self::Result {
|
||||
walk_variant(self, v)
|
||||
}
|
||||
fn visit_variant_discr(&mut self, discr: &'ast AnonConst) -> Self::Result {
|
||||
self.visit_anon_const(discr)
|
||||
}
|
||||
fn visit_label(&mut self, label: &'ast Label) -> Self::Result {
|
||||
walk_label(self, label)
|
||||
}
|
||||
fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) -> Self::Result {
|
||||
walk_lifetime(self, lifetime)
|
||||
}
|
||||
fn visit_mac_call(&mut self, mac: &'ast MacCall) -> Self::Result {
|
||||
walk_mac(self, mac)
|
||||
}
|
||||
fn visit_id(&mut self, _id: NodeId) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
fn visit_macro_def(&mut self, macro_def: &'ast MacroDef) -> Self::Result {
|
||||
walk_macro_def(self, macro_def)
|
||||
}
|
||||
fn visit_path(&mut self, path: &'ast Path) -> Self::Result {
|
||||
walk_path(self, path)
|
||||
}
|
||||
fn visit_use_tree(&mut self, use_tree: &'ast UseTree) -> Self::Result {
|
||||
walk_use_tree(self, use_tree)
|
||||
}
|
||||
fn visit_nested_use_tree(&mut self, use_tree: &'ast UseTree, id: NodeId) -> Self::Result {
|
||||
try_visit!(self.visit_id(id));
|
||||
self.visit_use_tree(use_tree)
|
||||
}
|
||||
fn visit_path_segment(&mut self, path_segment: &'ast PathSegment) -> Self::Result {
|
||||
walk_path_segment(self, path_segment)
|
||||
}
|
||||
fn visit_generic_args(&mut self, generic_args: &'ast GenericArgs) -> Self::Result {
|
||||
walk_generic_args(self, generic_args)
|
||||
}
|
||||
fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) -> Self::Result {
|
||||
walk_generic_arg(self, generic_arg)
|
||||
}
|
||||
fn visit_assoc_item_constraint(
|
||||
&mut self,
|
||||
constraint: &'ast AssocItemConstraint,
|
||||
) -> Self::Result {
|
||||
walk_assoc_item_constraint(self, constraint)
|
||||
}
|
||||
fn visit_attribute(&mut self, attr: &'ast Attribute) -> Self::Result {
|
||||
walk_attribute(self, attr)
|
||||
}
|
||||
fn visit_vis(&mut self, vis: &'ast Visibility) -> Self::Result {
|
||||
walk_vis(self, vis)
|
||||
}
|
||||
fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FnRetTy) -> Self::Result {
|
||||
walk_fn_ret_ty(self, ret_ty)
|
||||
}
|
||||
fn visit_fn_header(&mut self, header: &'ast FnHeader) -> Self::Result {
|
||||
walk_fn_header(self, header)
|
||||
}
|
||||
fn visit_expr_field(&mut self, f: &'ast ExprField) -> Self::Result {
|
||||
walk_expr_field(self, f)
|
||||
}
|
||||
fn visit_pat_field(&mut self, fp: &'ast PatField) -> Self::Result {
|
||||
walk_pat_field(self, fp)
|
||||
}
|
||||
fn visit_crate(&mut self, krate: &'ast Crate) -> Self::Result {
|
||||
walk_crate(self, krate)
|
||||
}
|
||||
fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) -> Self::Result {
|
||||
walk_inline_asm(self, asm)
|
||||
}
|
||||
fn visit_format_args(&mut self, fmt: &'ast FormatArgs) -> Self::Result {
|
||||
walk_format_args(self, fmt)
|
||||
}
|
||||
fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) -> Self::Result {
|
||||
walk_inline_asm_sym(self, sym)
|
||||
}
|
||||
fn visit_capture_by(&mut self, _capture_by: &'ast CaptureBy) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
fn visit_coroutine_kind(&mut self, coroutine_kind: &'ast CoroutineKind) -> Self::Result {
|
||||
walk_coroutine_kind(self, coroutine_kind)
|
||||
}
|
||||
fn visit_fn_decl(&mut self, fn_decl: &'ast FnDecl) -> Self::Result {
|
||||
walk_fn_decl(self, fn_decl)
|
||||
}
|
||||
fn visit_qself(&mut self, qs: &'ast Option<P<QSelf>>) -> Self::Result {
|
||||
walk_qself(self, qs)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! common_visitor_and_walkers {
|
||||
($(($mut: ident))? $Visitor:ident$(<$lt:lifetime>)?) => {
|
||||
$(${ignore($lt)}
|
||||
#[derive(Copy, Clone)]
|
||||
)?
|
||||
#[derive(Debug)]
|
||||
pub enum FnKind<'a> {
|
||||
/// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
|
||||
Fn(FnCtxt, &'a $($mut)? Visibility, &'a $($mut)? Fn),
|
||||
|
||||
/// E.g., `|x, y| body`.
|
||||
Closure(&'a $($mut)? ClosureBinder, &'a $($mut)? Option<CoroutineKind>, &'a $($mut)? P<FnDecl>, &'a $($mut)? P<Expr>),
|
||||
}
|
||||
|
||||
impl<'a> FnKind<'a> {
|
||||
pub fn header(&'a $($mut)? self) -> Option<&'a $($mut)? FnHeader> {
|
||||
match *self {
|
||||
FnKind::Fn(_, _, Fn { sig, .. }) => Some(&$($mut)? sig.header),
|
||||
FnKind::Closure(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ident(&'a $($mut)? self) -> Option<&'a $($mut)? Ident> {
|
||||
match self {
|
||||
FnKind::Fn(_, _, Fn { ident, .. }) => Some(ident),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decl(&'a $($mut)? self) -> &'a $($mut)? FnDecl {
|
||||
match self {
|
||||
FnKind::Fn(_, _, Fn { sig, .. }) => &$($mut)? sig.decl,
|
||||
FnKind::Closure(_, _, decl, _) => decl,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctxt(&self) -> Option<FnCtxt> {
|
||||
match self {
|
||||
FnKind::Fn(ctxt, ..) => Some(*ctxt),
|
||||
FnKind::Closure(..) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Each method of this trait is a hook to be potentially
|
||||
/// overridden. Each method's default implementation recursively visits
|
||||
/// the substructure of the input via the corresponding `walk` method;
|
||||
#[doc = concat!(" e.g., the `visit_item` method by default calls `visit"$(, "_", stringify!($mut))?, "::walk_item`.")]
|
||||
///
|
||||
/// If you want to ensure that your code handles every variant
|
||||
/// explicitly, you need to override each method. (And you also need
|
||||
/// to monitor future changes to this trait in case a new method with a
|
||||
/// new default implementation gets introduced.)
|
||||
///
|
||||
/// Every `walk_*` method uses deconstruction to access fields of structs and
|
||||
/// enums. This will result in a compile error if a field is added, which makes
|
||||
/// it more likely the appropriate visit call will be added for it.
|
||||
pub trait $Visitor<$($lt)?> : Sized $(${ignore($mut)} + MutVisitorResult<Result = ()>)? {
|
||||
$(
|
||||
${ignore($lt)}
|
||||
/// The result type of the `visit_*` methods. Can be either `()`,
|
||||
/// or `ControlFlow<T>`.
|
||||
type Result: VisitorResult = ();
|
||||
)?
|
||||
|
||||
// Methods in this trait have one of three forms, with the last two forms
|
||||
// only occuring on `MutVisitor`:
|
||||
//
|
||||
// fn visit_t(&mut self, t: &mut T); // common
|
||||
// fn flat_map_t(&mut self, t: T) -> SmallVec<[T; 1]>; // rare
|
||||
// fn filter_map_t(&mut self, t: T) -> Option<T>; // rarest
|
||||
//
|
||||
// When writing these methods, it is better to use destructuring like this:
|
||||
//
|
||||
// fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
|
||||
// visit_a(a);
|
||||
// visit_b(b);
|
||||
// }
|
||||
//
|
||||
// than to use field access like this:
|
||||
//
|
||||
// fn visit_abc(&mut self, abc: &mut ABC) {
|
||||
// visit_a(&mut abc.a);
|
||||
// visit_b(&mut abc.b);
|
||||
// // ignore abc.c
|
||||
// }
|
||||
//
|
||||
// As well as being more concise, the former is explicit about which fields
|
||||
// are skipped. Furthermore, if a new field is added, the destructuring
|
||||
// version will cause a compile error, which is good. In comparison, the
|
||||
// field access version will continue working and it would be easy to
|
||||
// forget to add handling for it.
|
||||
fn visit_ident(&mut self, Ident { name: _, span }: &$($lt)? $($mut)? Ident) -> Self::Result {
|
||||
visit_span(self, span)
|
||||
}
|
||||
|
||||
fn visit_foreign_mod(&mut self, nm: &$($lt)? $($mut)? ForeignMod) -> Self::Result {
|
||||
walk_foreign_mod(self, nm)
|
||||
}
|
||||
|
||||
fn visit_foreign_item(&mut self, i: &$($lt)? $($mut)? ForeignItem) -> Self::Result {
|
||||
walk_item(self, i)
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, i: &$($lt)? $($mut)? Item) -> Self::Result {
|
||||
walk_item(self, i)
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &$($lt)? $($mut)? Local) -> Self::Result {
|
||||
walk_local(self, l)
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, b: &$($lt)? $($mut)? Block) -> Self::Result {
|
||||
walk_block(self, b)
|
||||
}
|
||||
|
||||
fn visit_param(&mut self, param: &$($lt)? $($mut)? Param) -> Self::Result {
|
||||
walk_param(self, param)
|
||||
}
|
||||
|
||||
fn visit_arm(&mut self, a: &$($lt)? $($mut)? Arm) -> Self::Result {
|
||||
walk_arm(self, a)
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, p: &$($lt)? $($mut)? Pat) -> Self::Result {
|
||||
walk_pat(self, p)
|
||||
}
|
||||
|
||||
fn visit_anon_const(&mut self, c: &$($lt)? $($mut)? AnonConst) -> Self::Result {
|
||||
walk_anon_const(self, c)
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, ex: &$($lt)? $($mut)? Expr) -> Self::Result {
|
||||
walk_expr(self, ex)
|
||||
}
|
||||
|
||||
/// This method is a hack to workaround unstable of `stmt_expr_attributes`.
|
||||
/// It can be removed once that feature is stabilized.
|
||||
fn visit_method_receiver_expr(&mut self, ex: &$($lt)? $($mut)? Expr) -> Self::Result {
|
||||
self.visit_expr(ex)
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, t: &$($lt)? $($mut)? Ty) -> Self::Result {
|
||||
walk_ty(self, t)
|
||||
}
|
||||
|
||||
fn visit_ty_pat(&mut self, t: &$($lt)? $($mut)? TyPat) -> Self::Result {
|
||||
walk_ty_pat(self, t)
|
||||
}
|
||||
|
||||
fn visit_generic_param(&mut self, param: &$($lt)? $($mut)? GenericParam) -> Self::Result {
|
||||
walk_generic_param(self, param)
|
||||
}
|
||||
|
||||
fn visit_generics(&mut self, g: &$($lt)? $($mut)? Generics) -> Self::Result {
|
||||
walk_generics(self, g)
|
||||
}
|
||||
fn visit_closure_binder(&mut self, b: &$($lt)? $($mut)? ClosureBinder) -> Self::Result {
|
||||
walk_closure_binder(self, b)
|
||||
}
|
||||
fn visit_contract(&mut self, c: &$($lt)? $($mut)? FnContract) -> Self::Result {
|
||||
walk_contract(self, c)
|
||||
}
|
||||
|
||||
fn visit_where_predicate(&mut self, p: &$($lt)? $($mut)? WherePredicate) -> Self::Result {
|
||||
walk_where_predicate(self, p)
|
||||
}
|
||||
|
||||
fn visit_where_predicate_kind(&mut self, k: &$($lt)? $($mut)? WherePredicateKind) -> Self::Result {
|
||||
walk_where_predicate_kind(self, k)
|
||||
}
|
||||
|
||||
// for `MutVisitor`: `Span` and `NodeId` are mutated at the caller site.
|
||||
fn visit_fn(
|
||||
&mut self,
|
||||
fk: FnKind<$($lt)? $(${ignore($mut)} '_)?>,
|
||||
_: Span,
|
||||
_: NodeId
|
||||
) -> Self::Result {
|
||||
walk_fn(self, fk)
|
||||
}
|
||||
|
||||
fn visit_assoc_item(&mut self, i: &$($lt)? $($mut)? AssocItem, ctxt: AssocCtxt) -> Self::Result {
|
||||
walk_assoc_item(self, i, ctxt)
|
||||
}
|
||||
|
||||
fn visit_trait_ref(&mut self, t: &$($lt)? $($mut)? TraitRef) -> Self::Result {
|
||||
walk_trait_ref(self, t)
|
||||
}
|
||||
|
||||
fn visit_param_bound(&mut self, bounds: &$($lt)? $($mut)? GenericBound, _ctxt: BoundKind) -> Self::Result {
|
||||
walk_param_bound(self, bounds)
|
||||
}
|
||||
|
||||
fn visit_precise_capturing_arg(&mut self, arg: &$($lt)? $($mut)? PreciseCapturingArg) -> Self::Result {
|
||||
walk_precise_capturing_arg(self, arg)
|
||||
}
|
||||
|
||||
fn visit_poly_trait_ref(&mut self, t: &$($lt)? $($mut)? PolyTraitRef) -> Self::Result {
|
||||
walk_poly_trait_ref(self, t)
|
||||
}
|
||||
|
||||
fn visit_variant_data(&mut self, s: &$($lt)? $($mut)? VariantData) -> Self::Result {
|
||||
walk_variant_data(self, s)
|
||||
}
|
||||
|
||||
fn visit_field_def(&mut self, s: &$($lt)? $($mut)? FieldDef) -> Self::Result {
|
||||
walk_field_def(self, s)
|
||||
}
|
||||
|
||||
fn visit_variant(&mut self, v: &$($lt)? $($mut)? Variant) -> Self::Result {
|
||||
walk_variant(self, v)
|
||||
}
|
||||
|
||||
fn visit_label(&mut self, label: &$($lt)? $($mut)? Label) -> Self::Result {
|
||||
walk_label(self, label)
|
||||
}
|
||||
|
||||
fn visit_lifetime(&mut self, lifetime: &$($lt)? $($mut)? Lifetime, $(${ignore($lt)} _: LifetimeCtxt )?) -> Self::Result {
|
||||
walk_lifetime(self, lifetime)
|
||||
}
|
||||
|
||||
fn visit_mac_call(&mut self, mac: &$($lt)? $($mut)? MacCall) -> Self::Result {
|
||||
walk_mac(self, mac)
|
||||
}
|
||||
|
||||
fn visit_id(&mut self, _id: $(&$mut)? NodeId) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
|
||||
fn visit_macro_def(&mut self, macro_def: &$($lt)? $($mut)? MacroDef) -> Self::Result {
|
||||
walk_macro_def(self, macro_def)
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, path: &$($lt)? $($mut)? Path) -> Self::Result {
|
||||
walk_path(self, path)
|
||||
}
|
||||
|
||||
fn visit_use_tree(&mut self, use_tree: &$($lt)? $($mut)? UseTree) -> Self::Result {
|
||||
walk_use_tree(self, use_tree)
|
||||
}
|
||||
|
||||
fn visit_path_segment(&mut self, path_segment: &$($lt)? $($mut)? PathSegment) -> Self::Result {
|
||||
walk_path_segment(self, path_segment)
|
||||
}
|
||||
|
||||
fn visit_generic_args(&mut self, generic_args: &$($lt)? $($mut)? GenericArgs) -> Self::Result {
|
||||
walk_generic_args(self, generic_args)
|
||||
}
|
||||
|
||||
fn visit_generic_arg(&mut self, generic_arg: &$($lt)? $($mut)? GenericArg) -> Self::Result {
|
||||
walk_generic_arg(self, generic_arg)
|
||||
}
|
||||
|
||||
fn visit_assoc_item_constraint(
|
||||
&mut self,
|
||||
constraint: &$($lt)? $($mut)? AssocItemConstraint,
|
||||
) -> Self::Result {
|
||||
walk_assoc_item_constraint(self, constraint)
|
||||
}
|
||||
|
||||
fn visit_attribute(&mut self, attr: &$($lt)? $($mut)? Attribute) -> Self::Result {
|
||||
walk_attribute(self, attr)
|
||||
}
|
||||
|
||||
fn visit_vis(&mut self, vis: &$($lt)? $($mut)? Visibility) -> Self::Result {
|
||||
walk_vis(self, vis)
|
||||
}
|
||||
|
||||
fn visit_fn_ret_ty(&mut self, ret_ty: &$($lt)? $($mut)? FnRetTy) -> Self::Result {
|
||||
walk_fn_ret_ty(self, ret_ty)
|
||||
}
|
||||
|
||||
fn visit_fn_header(&mut self, header: &$($lt)? $($mut)? FnHeader) -> Self::Result {
|
||||
walk_fn_header(self, header)
|
||||
}
|
||||
|
||||
fn visit_expr_field(&mut self, f: &$($lt)? $($mut)? ExprField) -> Self::Result {
|
||||
walk_expr_field(self, f)
|
||||
}
|
||||
|
||||
fn visit_pat_field(&mut self, fp: &$($lt)? $($mut)? PatField) -> Self::Result {
|
||||
walk_pat_field(self, fp)
|
||||
}
|
||||
|
||||
fn visit_crate(&mut self, krate: &$($lt)? $($mut)? Crate) -> Self::Result {
|
||||
walk_crate(self, krate)
|
||||
}
|
||||
|
||||
fn visit_inline_asm(&mut self, asm: &$($lt)? $($mut)? InlineAsm) -> Self::Result {
|
||||
walk_inline_asm(self, asm)
|
||||
}
|
||||
|
||||
fn visit_format_args(&mut self, fmt: &$($lt)? $($mut)? FormatArgs) -> Self::Result {
|
||||
walk_format_args(self, fmt)
|
||||
}
|
||||
|
||||
fn visit_inline_asm_sym(&mut self, sym: &$($lt)? $($mut)? InlineAsmSym) -> Self::Result {
|
||||
walk_inline_asm_sym(self, sym)
|
||||
}
|
||||
|
||||
fn visit_capture_by(&mut self, capture_by: &$($lt)? $($mut)? CaptureBy) -> Self::Result {
|
||||
walk_capture_by(self, capture_by)
|
||||
}
|
||||
|
||||
fn visit_coroutine_kind(&mut self, coroutine_kind: &$($lt)? $($mut)? CoroutineKind) -> Self::Result {
|
||||
walk_coroutine_kind(self, coroutine_kind)
|
||||
}
|
||||
|
||||
fn visit_fn_decl(&mut self, fn_decl: &$($lt)? $($mut)? FnDecl) -> Self::Result {
|
||||
walk_fn_decl(self, fn_decl)
|
||||
}
|
||||
|
||||
fn visit_qself(&mut self, qs: &$($lt)? $($mut)? Option<P<QSelf>>) -> Self::Result {
|
||||
walk_qself(self, qs)
|
||||
}
|
||||
|
||||
// (non-mut) `Visitor`-only methods
|
||||
$(
|
||||
fn visit_stmt(&mut self, s: &$lt Stmt) -> Self::Result {
|
||||
walk_stmt(self, s)
|
||||
}
|
||||
|
||||
fn visit_nested_use_tree(&mut self, use_tree: &$lt UseTree, id: NodeId) -> Self::Result {
|
||||
try_visit!(self.visit_id(id));
|
||||
self.visit_use_tree(use_tree)
|
||||
}
|
||||
)?
|
||||
|
||||
// `MutVisitor`-only methods
|
||||
$(
|
||||
fn flat_map_foreign_item(&mut self, ni: P<ForeignItem>) -> SmallVec<[P<ForeignItem>; 1]> {
|
||||
walk_flat_map_foreign_item(self, ni)
|
||||
}
|
||||
|
||||
fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
|
||||
walk_flat_map_item(self, i)
|
||||
}
|
||||
|
||||
fn flat_map_field_def(&mut self, fd: FieldDef) -> SmallVec<[FieldDef; 1]> {
|
||||
walk_flat_map_field_def(self, fd)
|
||||
}
|
||||
|
||||
fn flat_map_assoc_item(
|
||||
&mut self,
|
||||
i: P<AssocItem>,
|
||||
ctxt: AssocCtxt,
|
||||
) -> SmallVec<[P<AssocItem>; 1]> {
|
||||
walk_flat_map_assoc_item(self, i, ctxt)
|
||||
}
|
||||
|
||||
fn flat_map_stmt(&mut self, s: Stmt) -> SmallVec<[Stmt; 1]> {
|
||||
walk_flat_map_stmt(self, s)
|
||||
}
|
||||
|
||||
fn flat_map_arm(&mut self, arm: Arm) -> SmallVec<[Arm; 1]> {
|
||||
walk_flat_map_arm(self, arm)
|
||||
}
|
||||
|
||||
fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
|
||||
walk_filter_map_expr(self, e)
|
||||
}
|
||||
|
||||
fn flat_map_variant(&mut self, v: Variant) -> SmallVec<[Variant; 1]> {
|
||||
walk_flat_map_variant(self, v)
|
||||
}
|
||||
|
||||
fn flat_map_param(&mut self, param: Param) -> SmallVec<[Param; 1]> {
|
||||
walk_flat_map_param(self, param)
|
||||
}
|
||||
|
||||
fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> {
|
||||
walk_flat_map_generic_param(self, param)
|
||||
}
|
||||
|
||||
fn flat_map_expr_field(&mut self, f: ExprField) -> SmallVec<[ExprField; 1]> {
|
||||
walk_flat_map_expr_field(self, f)
|
||||
}
|
||||
|
||||
fn flat_map_where_predicate(
|
||||
&mut self,
|
||||
where_predicate: WherePredicate,
|
||||
) -> SmallVec<[WherePredicate; 1]> {
|
||||
walk_flat_map_where_predicate(self, where_predicate)
|
||||
}
|
||||
|
||||
// Span visiting is no longer used, but we keep it for now,
|
||||
// in case it's needed for something like #127241.
|
||||
fn visit_span(&mut self, _sp: &$mut Span) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> {
|
||||
walk_flat_map_pat_field(self, fp)
|
||||
}
|
||||
)?
|
||||
}
|
||||
|
||||
pub trait WalkItemKind {
|
||||
type Ctxt;
|
||||
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
|
|
@ -409,6 +569,24 @@ macro_rules! common_visitor_and_walkers {
|
|||
V::Result::output()
|
||||
}
|
||||
|
||||
$(${ignore($lt)}
|
||||
#[inline]
|
||||
)?
|
||||
fn walk_capture_by<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
capture_by: &$($lt)? $($mut)? CaptureBy
|
||||
) -> V::Result {
|
||||
match capture_by {
|
||||
CaptureBy::Ref => { V::Result::output() }
|
||||
CaptureBy::Value { move_kw } => {
|
||||
visit_span(vis, move_kw)
|
||||
}
|
||||
CaptureBy::Use { use_kw } => {
|
||||
visit_span(vis, use_kw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_bounds<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, bounds: &$($lt)? $($mut)? GenericBounds, ctxt: BoundKind) -> V::Result {
|
||||
walk_list!(visitor, visit_param_bound, bounds, ctxt);
|
||||
V::Result::output()
|
||||
|
|
@ -989,8 +1167,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
try_visit!(vis.visit_vis(visibility));
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
try_visit!(vis.visit_variant_data(data));
|
||||
$(${ignore($lt)} visit_opt!(vis, visit_variant_discr, disr_expr); )?
|
||||
$(${ignore($mut)} visit_opt!(vis, visit_anon_const, disr_expr); )?
|
||||
visit_opt!(vis, visit_anon_const, disr_expr);
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
|
|
@ -1389,7 +1566,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
|
||||
// FIXME: visit the template exhaustively.
|
||||
pub fn walk_format_args<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, fmt: &$($lt)? $($mut)? FormatArgs) -> V::Result {
|
||||
let FormatArgs { span, template: _, arguments, uncooked_fmt_str: _ } = fmt;
|
||||
let FormatArgs { span, template: _, arguments, uncooked_fmt_str: _, is_source_literal: _ } = fmt;
|
||||
let args = $(${ignore($mut)} arguments.all_args_mut())? $(${ignore($lt)} arguments.all_args())? ;
|
||||
for FormatArgument { kind, expr } in args {
|
||||
match kind {
|
||||
|
|
|
|||
|
|
@ -172,9 +172,6 @@ ast_lowering_template_modifier = template modifier
|
|||
|
||||
ast_lowering_this_not_async = this is not `async`
|
||||
|
||||
ast_lowering_underscore_array_length_unstable =
|
||||
using `_` for array lengths is unstable
|
||||
|
||||
ast_lowering_underscore_expr_lhs_assign =
|
||||
in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
.label = `_` not allowed here
|
||||
|
|
|
|||
|
|
@ -2289,12 +2289,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
span: Span,
|
||||
elements: &'hir [hir::Expr<'hir>],
|
||||
) -> hir::Expr<'hir> {
|
||||
let addrof = hir::ExprKind::AddrOf(
|
||||
hir::BorrowKind::Ref,
|
||||
hir::Mutability::Not,
|
||||
self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements))),
|
||||
);
|
||||
self.expr(span, addrof)
|
||||
let array = self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements)));
|
||||
self.expr_ref(span, array)
|
||||
}
|
||||
|
||||
pub(super) fn expr_ref(&mut self, span: Span, expr: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
|
||||
self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr))
|
||||
}
|
||||
|
||||
pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
use core::ops::ControlFlow;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use rustc_ast::visit::Visitor;
|
||||
use rustc_ast::*;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_session::config::FmtDebug;
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
use rustc_span::{DesugaringKind, Ident, Span, Symbol, sym};
|
||||
|
||||
use super::LoweringContext;
|
||||
|
||||
|
|
@ -16,6 +14,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// format_args!() had any arguments _before_ flattening/inlining.
|
||||
let allow_const = fmt.arguments.all_args().is_empty();
|
||||
let mut fmt = Cow::Borrowed(fmt);
|
||||
|
||||
let sp = self.mark_span_with_reason(
|
||||
DesugaringKind::FormatLiteral { source: fmt.is_source_literal },
|
||||
sp,
|
||||
sp.ctxt().outer_expn_data().allow_internal_unstable,
|
||||
);
|
||||
|
||||
if self.tcx.sess.opts.unstable_opts.flatten_format_args {
|
||||
fmt = flatten_format_args(fmt);
|
||||
fmt = self.inline_literals(fmt);
|
||||
|
|
@ -476,77 +481,52 @@ fn expand_format_args<'hir>(
|
|||
return hir::ExprKind::Call(new, new_args);
|
||||
}
|
||||
|
||||
// If the args array contains exactly all the original arguments once,
|
||||
// in order, we can use a simple array instead of a `match` construction.
|
||||
// However, if there's a yield point in any argument except the first one,
|
||||
// we don't do this, because an Argument cannot be kept across yield points.
|
||||
//
|
||||
// This is an optimization, speeding up compilation about 1-2% in some cases.
|
||||
// See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
|
||||
let use_simple_array = argmap.len() == arguments.len()
|
||||
&& argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
|
||||
&& arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
|
||||
|
||||
let args = if arguments.is_empty() {
|
||||
let (let_statements, args) = if arguments.is_empty() {
|
||||
// Generate:
|
||||
// &<core::fmt::Argument>::none()
|
||||
// []
|
||||
(vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
|
||||
} else if argmap.len() == 1 && arguments.len() == 1 {
|
||||
// Only one argument, so we don't need to make the `args` tuple.
|
||||
//
|
||||
// Note:
|
||||
// `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime.
|
||||
//
|
||||
// This makes sure that this still fails to compile, even when the argument is inlined:
|
||||
//
|
||||
// ```
|
||||
// let f = format_args!("{}", "a");
|
||||
// println!("{f}"); // error E0716
|
||||
// ```
|
||||
//
|
||||
// Cases where keeping the object around is allowed, such as `format_args!("a")`,
|
||||
// are handled above by the `allow_const` case.
|
||||
let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||
macsp,
|
||||
hir::LangItem::FormatArgument,
|
||||
sym::none,
|
||||
));
|
||||
let none = ctx.expr_call(macsp, none_fn, &[]);
|
||||
ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none))
|
||||
} else if use_simple_array {
|
||||
// Generate:
|
||||
// &[
|
||||
// <core::fmt::Argument>::new_display(&arg0),
|
||||
// <core::fmt::Argument>::new_lower_hex(&arg1),
|
||||
// <core::fmt::Argument>::new_debug(&arg2),
|
||||
// …
|
||||
// ]
|
||||
let elements = ctx.arena.alloc_from_iter(arguments.iter().zip(argmap).map(
|
||||
|(arg, ((_, ty), placeholder_span))| {
|
||||
// super let args = [<core::fmt::Argument>::new_display(&arg)];
|
||||
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
|
||||
|(&(arg_index, ty), &placeholder_span)| {
|
||||
let arg = &arguments[arg_index];
|
||||
let placeholder_span =
|
||||
placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
|
||||
let arg_span = match arg.kind {
|
||||
FormatArgumentKind::Captured(_) => placeholder_span,
|
||||
_ => arg.expr.span.with_ctxt(macsp.ctxt()),
|
||||
};
|
||||
let arg = ctx.lower_expr(&arg.expr);
|
||||
let ref_arg = ctx.arena.alloc(ctx.expr(
|
||||
arg_span,
|
||||
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
|
||||
));
|
||||
let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg));
|
||||
make_argument(ctx, placeholder_span, ref_arg, ty)
|
||||
},
|
||||
));
|
||||
ctx.expr_array_ref(macsp, elements)
|
||||
} else {
|
||||
// Generate:
|
||||
// &match (&arg0, &arg1, &…) {
|
||||
// args => [
|
||||
// <core::fmt::Argument>::new_display(args.0),
|
||||
// <core::fmt::Argument>::new_lower_hex(args.1),
|
||||
// <core::fmt::Argument>::new_debug(args.0),
|
||||
// …
|
||||
// ]
|
||||
// }
|
||||
let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
|
||||
let args_ident = Ident::new(sym::args, macsp);
|
||||
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
|
||||
let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
|
||||
(vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)))
|
||||
} else {
|
||||
// Generate:
|
||||
// super let args = (&arg0, &arg1, &…);
|
||||
let args_ident = Ident::new(sym::args, macsp);
|
||||
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
|
||||
let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
|
||||
let arg_expr = ctx.lower_expr(&arg.expr);
|
||||
ctx.expr(
|
||||
arg.expr.span.with_ctxt(macsp.ctxt()),
|
||||
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
|
||||
)
|
||||
}));
|
||||
let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
|
||||
let let_statement_1 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args_tuple));
|
||||
|
||||
// Generate:
|
||||
// super let args = [
|
||||
// <core::fmt::Argument>::new_display(args.0),
|
||||
// <core::fmt::Argument>::new_lower_hex(args.1),
|
||||
// <core::fmt::Argument>::new_debug(args.0),
|
||||
// …
|
||||
// ];
|
||||
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
|
||||
|(&(arg_index, ty), &placeholder_span)| {
|
||||
let arg = &arguments[arg_index];
|
||||
|
|
@ -567,58 +547,47 @@ fn expand_format_args<'hir>(
|
|||
make_argument(ctx, placeholder_span, arg, ty)
|
||||
},
|
||||
));
|
||||
let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
|
||||
let arg_expr = ctx.lower_expr(&arg.expr);
|
||||
ctx.expr(
|
||||
arg.expr.span.with_ctxt(macsp.ctxt()),
|
||||
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
|
||||
)
|
||||
}));
|
||||
let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
|
||||
let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
|
||||
let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
|
||||
let match_expr = ctx.arena.alloc(ctx.expr_match(
|
||||
macsp,
|
||||
args_tuple,
|
||||
match_arms,
|
||||
hir::MatchSource::FormatArgs,
|
||||
));
|
||||
ctx.expr(
|
||||
macsp,
|
||||
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
|
||||
let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
|
||||
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
|
||||
let let_statement_2 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
|
||||
(
|
||||
vec![let_statement_1, let_statement_2],
|
||||
ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(format_options) = format_options {
|
||||
// Generate:
|
||||
// &args
|
||||
let args = ctx.expr_ref(macsp, args);
|
||||
|
||||
let call = if let Some(format_options) = format_options {
|
||||
// Generate:
|
||||
// <core::fmt::Arguments>::new_v1_formatted(
|
||||
// lit_pieces,
|
||||
// args,
|
||||
// format_options,
|
||||
// unsafe { ::core::fmt::UnsafeArg::new() }
|
||||
// )
|
||||
// unsafe {
|
||||
// <core::fmt::Arguments>::new_v1_formatted(
|
||||
// lit_pieces,
|
||||
// args,
|
||||
// format_options,
|
||||
// )
|
||||
// }
|
||||
let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||
macsp,
|
||||
hir::LangItem::FormatArguments,
|
||||
sym::new_v1_formatted,
|
||||
));
|
||||
let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||
macsp,
|
||||
hir::LangItem::FormatUnsafeArg,
|
||||
sym::new,
|
||||
));
|
||||
let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
|
||||
let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options]);
|
||||
let call = ctx.expr_call(macsp, new_v1_formatted, args);
|
||||
let hir_id = ctx.next_id();
|
||||
let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
|
||||
stmts: &[],
|
||||
expr: Some(unsafe_arg_new_call),
|
||||
hir_id,
|
||||
rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
|
||||
span: macsp,
|
||||
targeted_by_break: false,
|
||||
}));
|
||||
let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
|
||||
hir::ExprKind::Call(new_v1_formatted, args)
|
||||
hir::ExprKind::Block(
|
||||
ctx.arena.alloc(hir::Block {
|
||||
stmts: &[],
|
||||
expr: Some(call),
|
||||
hir_id,
|
||||
rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
|
||||
span: macsp,
|
||||
targeted_by_break: false,
|
||||
}),
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
// Generate:
|
||||
// <core::fmt::Arguments>::new_v1(
|
||||
|
|
@ -632,37 +601,23 @@ fn expand_format_args<'hir>(
|
|||
));
|
||||
let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
|
||||
hir::ExprKind::Call(new_v1, new_args)
|
||||
};
|
||||
|
||||
if !let_statements.is_empty() {
|
||||
// Generate:
|
||||
// {
|
||||
// super let …
|
||||
// super let …
|
||||
// <core::fmt::Arguments>::new_…(…)
|
||||
// }
|
||||
let call = ctx.arena.alloc(ctx.expr(macsp, call));
|
||||
let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call));
|
||||
hir::ExprKind::Block(block, None)
|
||||
} else {
|
||||
call
|
||||
}
|
||||
}
|
||||
|
||||
fn may_contain_yield_point(e: &ast::Expr) -> bool {
|
||||
struct MayContainYieldPoint;
|
||||
|
||||
impl Visitor<'_> for MayContainYieldPoint {
|
||||
type Result = ControlFlow<()>;
|
||||
|
||||
fn visit_expr(&mut self, e: &ast::Expr) -> ControlFlow<()> {
|
||||
if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
visit::walk_expr(self, e)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mac_call(&mut self, _: &ast::MacCall) -> ControlFlow<()> {
|
||||
// Macros should be expanded at this point.
|
||||
unreachable!("unexpanded macro in ast lowering");
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, _: &ast::Item) -> ControlFlow<()> {
|
||||
// Do not recurse into nested items.
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
MayContainYieldPoint.visit_expr(e).is_break()
|
||||
}
|
||||
|
||||
fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {
|
||||
for piece in template {
|
||||
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ use rustc_data_structures::sorted_map::SortedMap;
|
|||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::spawn;
|
||||
use rustc_data_structures::tagged_ptr::TaggedRef;
|
||||
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
|
||||
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle};
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::lints::DelayedLint;
|
||||
|
|
@ -60,7 +60,7 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
|
|||
use rustc_macros::extension;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
||||
use rustc_session::parse::add_feature_diagnostics;
|
||||
use rustc_span::symbol::{Ident, Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Span};
|
||||
use smallvec::SmallVec;
|
||||
|
|
@ -2109,15 +2109,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// `ExprKind::Paren(ExprKind::Underscore)` and should also be lowered to `GenericArg::Infer`
|
||||
match c.value.peel_parens().kind {
|
||||
ExprKind::Underscore => {
|
||||
if !self.tcx.features().generic_arg_infer() {
|
||||
feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::generic_arg_infer,
|
||||
c.value.span,
|
||||
fluent_generated::ast_lowering_underscore_array_length_unstable,
|
||||
)
|
||||
.stash(c.value.span, StashKey::UnderscoreForArrayLengths);
|
||||
}
|
||||
let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span), ());
|
||||
self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
|
||||
}
|
||||
|
|
@ -2301,6 +2292,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
|
||||
}
|
||||
|
||||
fn stmt_super_let_pat(
|
||||
&mut self,
|
||||
span: Span,
|
||||
pat: &'hir hir::Pat<'hir>,
|
||||
init: Option<&'hir hir::Expr<'hir>>,
|
||||
) -> hir::Stmt<'hir> {
|
||||
let hir_id = self.next_id();
|
||||
let local = hir::LetStmt {
|
||||
super_: Some(span),
|
||||
hir_id,
|
||||
init,
|
||||
pat,
|
||||
els: None,
|
||||
source: hir::LocalSource::Normal,
|
||||
span: self.lower_span(span),
|
||||
ty: None,
|
||||
};
|
||||
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
|
||||
}
|
||||
|
||||
fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
|
||||
self.block_all(expr.span, &[], Some(expr))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -386,18 +386,44 @@ impl<'a> State<'a> {
|
|||
|
||||
let ib = self.ibox(INDENT_UNIT);
|
||||
|
||||
// The Match subexpression in `match x {} - 1` must be parenthesized if
|
||||
// it is the leftmost subexpression in a statement:
|
||||
//
|
||||
// (match x {}) - 1;
|
||||
//
|
||||
// But not otherwise:
|
||||
//
|
||||
// let _ = match x {} - 1;
|
||||
//
|
||||
// Same applies to a small set of other expression kinds which eagerly
|
||||
// terminate a statement which opens with them.
|
||||
let needs_par = fixup.would_cause_statement_boundary(expr);
|
||||
let needs_par = {
|
||||
// The Match subexpression in `match x {} - 1` must be parenthesized
|
||||
// if it is the leftmost subexpression in a statement:
|
||||
//
|
||||
// (match x {}) - 1;
|
||||
//
|
||||
// But not otherwise:
|
||||
//
|
||||
// let _ = match x {} - 1;
|
||||
//
|
||||
// Same applies to a small set of other expression kinds which
|
||||
// eagerly terminate a statement which opens with them.
|
||||
fixup.would_cause_statement_boundary(expr)
|
||||
} || {
|
||||
// If a binary operation ends up with an attribute, such as
|
||||
// resulting from the following macro expansion, then parentheses
|
||||
// are required so that the attribute encompasses the right
|
||||
// subexpression and not just the left one.
|
||||
//
|
||||
// #![feature(stmt_expr_attributes)]
|
||||
//
|
||||
// macro_rules! add_attr {
|
||||
// ($e:expr) => { #[attr] $e };
|
||||
// }
|
||||
//
|
||||
// let _ = add_attr!(1 + 1);
|
||||
//
|
||||
// We must pretty-print `#[attr] (1 + 1)` not `#[attr] 1 + 1`.
|
||||
!attrs.is_empty()
|
||||
&& matches!(
|
||||
expr.kind,
|
||||
ast::ExprKind::Binary(..)
|
||||
| ast::ExprKind::Cast(..)
|
||||
| ast::ExprKind::Assign(..)
|
||||
| ast::ExprKind::AssignOp(..)
|
||||
| ast::ExprKind::Range(..)
|
||||
)
|
||||
};
|
||||
if needs_par {
|
||||
self.popen();
|
||||
fixup = FixupContext::default();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use thin_vec::ThinVec;
|
|||
|
||||
use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
|
||||
pub enum InlineAttr {
|
||||
None,
|
||||
Hint,
|
||||
|
|
@ -38,7 +38,8 @@ pub enum InstructionSetAttr {
|
|||
ArmT32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)]
|
||||
#[derive(Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum OptimizeAttr {
|
||||
/// No `#[optimize(..)]` attribute
|
||||
#[default]
|
||||
|
|
@ -182,6 +183,9 @@ impl Deprecation {
|
|||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub enum AttributeKind {
|
||||
// tidy-alphabetical-start
|
||||
/// Represents `#[align(N)]`.
|
||||
Align { align: Align, span: Span },
|
||||
|
||||
/// Represents `#[rustc_allow_const_fn_unstable]`.
|
||||
AllowConstFnUnstable(ThinVec<Symbol>),
|
||||
|
||||
|
|
@ -198,6 +202,9 @@ pub enum AttributeKind {
|
|||
span: Span,
|
||||
},
|
||||
|
||||
/// Represents `#[cold]`.
|
||||
Cold(Span),
|
||||
|
||||
/// Represents `#[rustc_confusables]`.
|
||||
Confusables {
|
||||
symbols: ThinVec<Symbol>,
|
||||
|
|
@ -221,9 +228,31 @@ pub enum AttributeKind {
|
|||
/// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
|
||||
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
|
||||
|
||||
/// Represents `#[inline]` and `#[rustc_force_inline]`.
|
||||
Inline(InlineAttr, Span),
|
||||
|
||||
/// Represents `#[rustc_macro_transparency]`.
|
||||
MacroTransparency(Transparency),
|
||||
|
||||
/// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html).
|
||||
MayDangle(Span),
|
||||
|
||||
/// Represents `#[must_use]`.
|
||||
MustUse {
|
||||
span: Span,
|
||||
/// must_use can optionally have a reason: `#[must_use = "reason this must be used"]`
|
||||
reason: Option<Symbol>,
|
||||
},
|
||||
|
||||
/// Represents `#[no_mangle]`
|
||||
NoMangle(Span),
|
||||
|
||||
/// Represents `#[optimize(size|speed)]`
|
||||
Optimize(OptimizeAttr, Span),
|
||||
|
||||
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
|
||||
PubTransparent(Span),
|
||||
|
||||
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
|
||||
Repr(ThinVec<(ReprAttr, Span)>),
|
||||
|
||||
|
|
|
|||
|
|
@ -11,4 +11,5 @@ pub struct AttributeLint<Id> {
|
|||
#[derive(Clone, Debug, HashStable_Generic)]
|
||||
pub enum AttributeLintKind {
|
||||
UnusedDuplicate { this: Span, other: Span, warning: bool },
|
||||
IllFormedAttributeInput { suggestions: Vec<String> },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ attr_parsing_expects_feature_list =
|
|||
attr_parsing_expects_features =
|
||||
`{$name}` expects feature names
|
||||
|
||||
attr_parsing_incorrect_meta_item = expected a quoted string literal
|
||||
attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
|
||||
attr_parsing_ill_formed_attribute_input = {$num_suggestions ->
|
||||
[1] attribute must be of the form {$suggestions}
|
||||
*[other] valid forms for the attribute are {$suggestions}
|
||||
}
|
||||
|
||||
attr_parsing_incorrect_repr_format_align_one_arg =
|
||||
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
|
||||
|
|
@ -42,6 +44,9 @@ attr_parsing_incorrect_repr_format_packed_expect_integer =
|
|||
attr_parsing_incorrect_repr_format_packed_one_or_zero_arg =
|
||||
incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all
|
||||
|
||||
attr_parsing_invalid_alignment_value =
|
||||
invalid alignment value: {$error_part}
|
||||
|
||||
attr_parsing_invalid_issue_string =
|
||||
`issue` must be a non-zero numeric string or "none"
|
||||
.must_not_be_zero = `issue` must not be "0", use "none" instead
|
||||
|
|
@ -81,9 +86,6 @@ attr_parsing_missing_note =
|
|||
attr_parsing_missing_since =
|
||||
missing 'since'
|
||||
|
||||
attr_parsing_multiple_item =
|
||||
multiple '{$item}' items
|
||||
|
||||
attr_parsing_multiple_stability_levels =
|
||||
multiple stability levels
|
||||
|
||||
|
|
@ -122,10 +124,6 @@ attr_parsing_unsupported_literal_cfg_boolean =
|
|||
literal in `cfg` predicate value must be a boolean
|
||||
attr_parsing_unsupported_literal_cfg_string =
|
||||
literal in `cfg` predicate value must be a string
|
||||
attr_parsing_unsupported_literal_deprecated_kv_pair =
|
||||
item in `deprecated` must be a key/value pair
|
||||
attr_parsing_unsupported_literal_deprecated_string =
|
||||
literal in `deprecated` value must be a string
|
||||
attr_parsing_unsupported_literal_generic =
|
||||
unsupported literal
|
||||
attr_parsing_unsupported_literal_suggestion =
|
||||
|
|
@ -136,6 +134,7 @@ attr_parsing_unused_duplicate =
|
|||
.suggestion = remove this attribute
|
||||
.note = attribute also specified here
|
||||
.warn = {-passes_previously_accepted}
|
||||
|
||||
attr_parsing_unused_multiple =
|
||||
multiple `{$name}` attributes
|
||||
.suggestion = remove this attribute
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::iter;
|
||||
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
|
|
@ -13,6 +14,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
|
|||
const PATH: &[Symbol] = &[sym::allow_internal_unstable];
|
||||
type Item = (Symbol, Span);
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
@ -29,6 +31,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
|
|||
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
|
||||
type Item = Symbol;
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
|
|||
76
compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Normal file
76
compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) struct OptimizeParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::optimize];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(single) = list.single() else {
|
||||
cx.expected_single_argument(list.span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
|
||||
Some(sym::size) => OptimizeAttr::Size,
|
||||
Some(sym::speed) => OptimizeAttr::Speed,
|
||||
Some(sym::none) => OptimizeAttr::DoNotOptimize,
|
||||
_ => {
|
||||
cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
|
||||
OptimizeAttr::Default
|
||||
}
|
||||
};
|
||||
|
||||
Some(AttributeKind::Optimize(res, cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ColdParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for ColdParser {
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::cold];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
if !args.no_args() {
|
||||
cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::Cold(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct NoMangleParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::no_mangle];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
if !args.no_args() {
|
||||
cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::NoMangle(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_feature::template;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
|
|
@ -13,37 +14,33 @@ pub(crate) struct ConfusablesParser {
|
|||
}
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for ConfusablesParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(&[sym::rustc_confusables], |this, cx, args| {
|
||||
let Some(list) = args.list() else {
|
||||
// FIXME(jdonszelmann): error when not a list? Bring validation code here.
|
||||
// NOTE: currently subsequent attributes are silently ignored using
|
||||
// tcx.get_attr().
|
||||
return;
|
||||
};
|
||||
|
||||
if list.is_empty() {
|
||||
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
|
||||
}
|
||||
|
||||
for param in list.mixed() {
|
||||
let span = param.span();
|
||||
|
||||
let Some(lit) = param.lit() else {
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem {
|
||||
span,
|
||||
suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
|
||||
lo: span.shrink_to_lo(),
|
||||
hi: span.shrink_to_hi(),
|
||||
}),
|
||||
});
|
||||
continue;
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::rustc_confusables],
|
||||
template!(List: r#""name1", "name2", ..."#),
|
||||
|this, cx, args| {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return;
|
||||
};
|
||||
|
||||
this.confusables.push(lit.symbol);
|
||||
}
|
||||
if list.is_empty() {
|
||||
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
|
||||
}
|
||||
|
||||
this.first_span.get_or_insert(cx.attr_span);
|
||||
})];
|
||||
for param in list.mixed() {
|
||||
let span = param.span();
|
||||
|
||||
let Some(lit) = param.lit().and_then(|i| i.value_str()) else {
|
||||
cx.expected_string_literal(span, param.lit());
|
||||
continue;
|
||||
};
|
||||
|
||||
this.confusables.push(lit);
|
||||
}
|
||||
|
||||
this.first_span.get_or_insert(cx.attr_span);
|
||||
},
|
||||
)];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if self.confusables.is_empty() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::util::parse_version;
|
||||
|
|
@ -6,7 +7,6 @@ use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
|||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
use crate::session_diagnostics::UnsupportedLiteralReason;
|
||||
|
||||
pub(crate) struct DeprecationParser;
|
||||
|
||||
|
|
@ -18,25 +18,18 @@ fn get<S: Stage>(
|
|||
item: &Option<Symbol>,
|
||||
) -> Option<Symbol> {
|
||||
if item.is_some() {
|
||||
cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() });
|
||||
cx.duplicate_key(param_span, name);
|
||||
return None;
|
||||
}
|
||||
if let Some(v) = arg.name_value() {
|
||||
if let Some(value_str) = v.value_as_str() {
|
||||
Some(value_str)
|
||||
} else {
|
||||
let lit = v.value_as_lit();
|
||||
cx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: v.value_span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedString,
|
||||
is_bytestr: lit.kind.is_bytestr(),
|
||||
start_point_span: cx.sess().source_map().start_point(lit.span),
|
||||
});
|
||||
cx.expected_string_literal(v.value_span, Some(&v.value_as_lit()));
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// FIXME(jdonszelmann): suggestion?
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
|
||||
cx.expected_name_value(param_span, Some(name));
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +38,11 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
|
|||
const PATH: &[Symbol] = &[sym::deprecated];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
Word,
|
||||
List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
|
||||
NameValueStr: "reason"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let features = cx.features();
|
||||
|
|
@ -55,57 +53,60 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
|
|||
|
||||
let is_rustc = features.staged_api();
|
||||
|
||||
if let Some(value) = args.name_value()
|
||||
&& let Some(value_str) = value.value_as_str()
|
||||
{
|
||||
note = Some(value_str)
|
||||
} else if let Some(list) = args.list() {
|
||||
for param in list.mixed() {
|
||||
let param_span = param.span();
|
||||
let Some(param) = param.meta_item() else {
|
||||
cx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: param_span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedKvPair,
|
||||
is_bytestr: false,
|
||||
start_point_span: cx.sess().source_map().start_point(param_span),
|
||||
});
|
||||
return None;
|
||||
};
|
||||
|
||||
let ident_name = param.path().word_sym();
|
||||
|
||||
match ident_name {
|
||||
Some(name @ sym::since) => {
|
||||
since = Some(get(cx, name, param_span, param.args(), &since)?);
|
||||
}
|
||||
Some(name @ sym::note) => {
|
||||
note = Some(get(cx, name, param_span, param.args(), ¬e)?);
|
||||
}
|
||||
Some(name @ sym::suggestion) => {
|
||||
if !features.deprecated_suggestion() {
|
||||
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
|
||||
span: param_span,
|
||||
is_nightly: cx.sess().is_nightly_build(),
|
||||
details: (),
|
||||
});
|
||||
}
|
||||
|
||||
suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?);
|
||||
}
|
||||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
item: param.path().to_string(),
|
||||
expected: if features.deprecated_suggestion() {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
&["since", "note"]
|
||||
},
|
||||
});
|
||||
match args {
|
||||
ArgParser::NoArgs => {
|
||||
// ok
|
||||
}
|
||||
ArgParser::List(list) => {
|
||||
for param in list.mixed() {
|
||||
let Some(param) = param.meta_item() else {
|
||||
cx.unexpected_literal(param.span());
|
||||
return None;
|
||||
};
|
||||
|
||||
let ident_name = param.path().word_sym();
|
||||
|
||||
match ident_name {
|
||||
Some(name @ sym::since) => {
|
||||
since = Some(get(cx, name, param.span(), param.args(), &since)?);
|
||||
}
|
||||
Some(name @ sym::note) => {
|
||||
note = Some(get(cx, name, param.span(), param.args(), ¬e)?);
|
||||
}
|
||||
Some(name @ sym::suggestion) => {
|
||||
if !features.deprecated_suggestion() {
|
||||
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
|
||||
span: param.span(),
|
||||
is_nightly: cx.sess().is_nightly_build(),
|
||||
details: (),
|
||||
});
|
||||
}
|
||||
|
||||
suggestion =
|
||||
Some(get(cx, name, param.span(), param.args(), &suggestion)?);
|
||||
}
|
||||
_ => {
|
||||
cx.unknown_key(
|
||||
param.span(),
|
||||
param.path().to_string(),
|
||||
if features.deprecated_suggestion() {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
&["since", "note"]
|
||||
},
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(v) => {
|
||||
let Some(value) = v.value_as_str() else {
|
||||
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
|
||||
return None;
|
||||
};
|
||||
note = Some(value);
|
||||
}
|
||||
}
|
||||
|
||||
let since = if let Some(since) = since {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
|
|||
return None;
|
||||
};
|
||||
|
||||
match l.meta_item().and_then(|i| i.word_without_args().map(|i| i.name)) {
|
||||
match l.meta_item().and_then(|i| i.path().word_sym()) {
|
||||
Some(sym::always) => {
|
||||
Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span))
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
|
|||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason");
|
||||
|
||||
fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let reason = match args {
|
||||
ArgParser::NoArgs => None,
|
||||
ArgParser::List(list) => {
|
||||
|
|
@ -73,7 +73,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
|
|||
};
|
||||
|
||||
let Some(reason) = l.lit().and_then(|i| i.kind.str()) else {
|
||||
cx.expected_string_literal(l.span());
|
||||
cx.expected_string_literal(l.span(), l.lit());
|
||||
return None;
|
||||
};
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
|
|||
}
|
||||
ArgParser::NameValue(v) => {
|
||||
let Some(reason) = v.value_as_str() else {
|
||||
cx.expected_string_literal(v.value_span);
|
||||
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
|
||||
return None;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
|
|
@ -9,13 +10,25 @@ pub(crate) struct AsPtrParser;
|
|||
|
||||
impl<S: Stage> SingleAttributeParser<S> for AsPtrParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_as_ptr];
|
||||
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
|
||||
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
// FIXME: check that there's no args (this is currently checked elsewhere)
|
||||
Some(AttributeKind::AsPtr(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PubTransparentParser;
|
||||
impl<S: Stage> SingleAttributeParser<S> for PubTransparentParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_pub_transparent];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
// FIXME: check that there's no args (this is currently checked elsewhere)
|
||||
Some(AttributeKind::PubTransparent(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use std::marker::PhantomData;
|
|||
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_attr_data_structures::lints::AttributeLintKind;
|
||||
use rustc_feature::AttributeTemplate;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
|
|
@ -27,16 +28,20 @@ use crate::session_diagnostics::UnusedMultiple;
|
|||
|
||||
pub(crate) mod allow_unstable;
|
||||
pub(crate) mod cfg;
|
||||
pub(crate) mod codegen_attrs;
|
||||
pub(crate) mod confusables;
|
||||
pub(crate) mod deprecation;
|
||||
pub(crate) mod inline;
|
||||
pub(crate) mod lint_helpers;
|
||||
pub(crate) mod must_use;
|
||||
pub(crate) mod repr;
|
||||
pub(crate) mod semantics;
|
||||
pub(crate) mod stability;
|
||||
pub(crate) mod transparency;
|
||||
pub(crate) mod util;
|
||||
|
||||
type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>);
|
||||
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AcceptFn<T, S>)];
|
||||
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AttributeTemplate, AcceptFn<T, S>)];
|
||||
|
||||
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
|
||||
///
|
||||
|
|
@ -84,14 +89,30 @@ pub(crate) trait AttributeParser<S: Stage>: Default + 'static {
|
|||
/// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
|
||||
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
|
||||
pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
|
||||
/// The single path of the attribute this parser accepts.
|
||||
///
|
||||
/// If you need the parser to accept more than one path, use [`AttributeParser`] instead
|
||||
const PATH: &[Symbol];
|
||||
|
||||
/// Configures the precedence of attributes with the same `PATH` on a syntax node.
|
||||
const ATTRIBUTE_ORDER: AttributeOrder;
|
||||
|
||||
/// Configures what to do when when the same attribute is
|
||||
/// applied more than once on the same syntax node.
|
||||
///
|
||||
/// [`ATTRIBUTE_ORDER`](Self::ATTRIBUTE_ORDER) specified which one is assumed to be correct,
|
||||
/// and this specified whether to, for example, warn or error on the other one.
|
||||
const ON_DUPLICATE: OnDuplicate<S>;
|
||||
|
||||
/// The template this attribute parser should implement. Used for diagnostics.
|
||||
const TEMPLATE: AttributeTemplate;
|
||||
|
||||
/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
|
||||
}
|
||||
|
||||
/// Use in combination with [`SingleAttributeParser`].
|
||||
/// `Single<T: SingleAttributeParser>` implements [`AttributeParser`].
|
||||
pub(crate) struct Single<T: SingleAttributeParser<S>, S: Stage>(
|
||||
PhantomData<(S, T)>,
|
||||
Option<(AttributeKind, Span)>,
|
||||
|
|
@ -104,8 +125,10 @@ impl<T: SingleAttributeParser<S>, S: Stage> Default for Single<T, S> {
|
|||
}
|
||||
|
||||
impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> =
|
||||
&[(T::PATH, |group: &mut Single<T, S>, cx, args| {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
T::PATH,
|
||||
<T as SingleAttributeParser<S>>::TEMPLATE,
|
||||
|group: &mut Single<T, S>, cx, args| {
|
||||
if let Some(pa) = T::convert(cx, args) {
|
||||
match T::ATTRIBUTE_ORDER {
|
||||
// keep the first and report immediately. ignore this attribute
|
||||
|
|
@ -126,7 +149,8 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S>
|
|||
|
||||
group.1 = Some((pa, cx.attr_span));
|
||||
}
|
||||
})];
|
||||
},
|
||||
)];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
Some(self.1?.0)
|
||||
|
|
@ -221,8 +245,15 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
|
|||
const PATH: &[rustc_span::Symbol];
|
||||
|
||||
type Item;
|
||||
/// A function that converts individual items (of type [`Item`](Self::Item)) into the final attribute.
|
||||
///
|
||||
/// For example, individual representations fomr `#[repr(...)]` attributes into an `AttributeKind::Repr(x)`,
|
||||
/// where `x` is a vec of these individual reprs.
|
||||
const CONVERT: ConvertFn<Self::Item>;
|
||||
|
||||
/// The template this attribute parser should implement. Used for diagnostics.
|
||||
const TEMPLATE: AttributeTemplate;
|
||||
|
||||
/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
@ -230,6 +261,8 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
|
|||
) -> impl IntoIterator<Item = Self::Item> + 'c;
|
||||
}
|
||||
|
||||
/// Use in combination with [`CombineAttributeParser`].
|
||||
/// `Combine<T: CombineAttributeParser>` implements [`AttributeParser`].
|
||||
pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>(
|
||||
PhantomData<(S, T)>,
|
||||
ThinVec<<T as CombineAttributeParser<S>>::Item>,
|
||||
|
|
@ -242,8 +275,11 @@ impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> {
|
|||
}
|
||||
|
||||
impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> =
|
||||
&[(T::PATH, |group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)))];
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
T::PATH,
|
||||
<T as CombineAttributeParser<S>>::TEMPLATE,
|
||||
|group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)),
|
||||
)];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
|
||||
|
|
|
|||
40
compiler/rustc_attr_parsing/src/attributes/must_use.rs
Normal file
40
compiler/rustc_attr_parsing/src/attributes/must_use.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_errors::DiagArgValue;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
|
||||
pub(crate) struct MustUseParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
|
||||
const PATH: &[Symbol] = &[sym::must_use];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::MustUse {
|
||||
span: cx.attr_span,
|
||||
reason: match args {
|
||||
ArgParser::NoArgs => None,
|
||||
ArgParser::NameValue(name_value) => name_value.value_as_str(),
|
||||
ArgParser::List(_) => {
|
||||
let suggestions =
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
|
||||
cx.emit_err(session_diagnostics::MustUseIllFormedAttributeInput {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
|
||||
),
|
||||
span: cx.attr_span,
|
||||
});
|
||||
return None;
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
use rustc_abi::Align;
|
||||
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
|
||||
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
|
||||
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
use super::{AcceptMapping, AttributeParser, CombineAttributeParser, ConvertFn, FinalizeContext};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
|
||||
use crate::session_diagnostics;
|
||||
|
|
@ -23,6 +24,9 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
|
|||
type Item = (ReprAttr, Span);
|
||||
const PATH: &[Symbol] = &[sym::repr];
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
|
||||
// FIXME(jdonszelmann): never used
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
@ -31,6 +35,7 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
|
|||
let mut reprs = Vec::new();
|
||||
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return reprs;
|
||||
};
|
||||
|
||||
|
|
@ -199,7 +204,7 @@ fn parse_repr_align<S: Stage>(
|
|||
});
|
||||
}
|
||||
Align => {
|
||||
cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
|
||||
span: param_span,
|
||||
});
|
||||
}
|
||||
|
|
@ -262,3 +267,57 @@ fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
|
|||
Err("not an unsuffixed integer")
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse #[align(N)].
|
||||
#[derive(Default)]
|
||||
pub(crate) struct AlignParser(Option<(Align, Span)>);
|
||||
|
||||
impl AlignParser {
|
||||
const PATH: &'static [Symbol] = &[sym::align];
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "<alignment in bytes>");
|
||||
|
||||
fn parse<'c, S: Stage>(
|
||||
&mut self,
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) {
|
||||
match args {
|
||||
ArgParser::NoArgs | ArgParser::NameValue(_) => {
|
||||
cx.expected_list(cx.attr_span);
|
||||
}
|
||||
ArgParser::List(list) => {
|
||||
let Some(align) = list.single() else {
|
||||
cx.expected_single_argument(list.span);
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(lit) = align.lit() else {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
|
||||
span: align.span(),
|
||||
});
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
match parse_alignment(&lit.kind) {
|
||||
Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
|
||||
Err(message) => {
|
||||
cx.emit_err(session_diagnostics::InvalidAlignmentValue {
|
||||
span: lit.span,
|
||||
error_part: message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for AlignParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
let (align, span) = self.0?;
|
||||
Some(AttributeKind::Align { align, span })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
compiler/rustc_attr_parsing/src/attributes/semantics.rs
Normal file
19
compiler/rustc_attr_parsing/src/attributes/semantics.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) struct MayDangleParser;
|
||||
impl<S: Stage> SingleAttributeParser<S> for MayDangleParser {
|
||||
const PATH: &[Symbol] = &[sym::may_dangle];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::MayDangle(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,8 @@ use rustc_attr_data_structures::{
|
|||
StableSince, UnstableReason, VERSION_PLACEHOLDER,
|
||||
};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
|
||||
use super::util::parse_version;
|
||||
use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
|
||||
|
|
@ -43,26 +44,39 @@ impl StabilityParser {
|
|||
|
||||
impl<S: Stage> AttributeParser<S> for StabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[
|
||||
(&[sym::stable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_stability(cx, args)
|
||||
{
|
||||
this.stability = Some((Stability { level, feature }, cx.attr_span));
|
||||
}
|
||||
}),
|
||||
(&[sym::unstable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_unstability(cx, args)
|
||||
{
|
||||
this.stability = Some((Stability { level, feature }, cx.attr_span));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_allowed_through_unstable_modules], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
this.allowed_through_unstable_modules = args.name_value().and_then(|i| i.value_as_str())
|
||||
}),
|
||||
(
|
||||
&[sym::stable],
|
||||
template!(List: r#"feature = "name", since = "version""#),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_stability(cx, args)
|
||||
{
|
||||
this.stability = Some((Stability { level, feature }, cx.attr_span));
|
||||
}
|
||||
},
|
||||
),
|
||||
(
|
||||
&[sym::unstable],
|
||||
template!(List: r#"feature = "name", reason = "...", issue = "N""#),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_unstability(cx, args)
|
||||
{
|
||||
this.stability = Some((Stability { level, feature }, cx.attr_span));
|
||||
}
|
||||
},
|
||||
),
|
||||
(
|
||||
&[sym::rustc_allowed_through_unstable_modules],
|
||||
template!(NameValueStr: "deprecation message"),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
this.allowed_through_unstable_modules =
|
||||
args.name_value().and_then(|i| i.value_as_str())
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
|
|
@ -96,8 +110,10 @@ pub(crate) struct BodyStabilityParser {
|
|||
}
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> =
|
||||
&[(&[sym::rustc_default_body_unstable], |this, cx, args| {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::rustc_default_body_unstable],
|
||||
template!(List: r#"feature = "name", reason = "...", issue = "N""#),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if this.stability.is_some() {
|
||||
cx.dcx()
|
||||
|
|
@ -105,7 +121,8 @@ impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
|
|||
} else if let Some((feature, level)) = parse_unstability(cx, args) {
|
||||
this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
|
||||
}
|
||||
})];
|
||||
},
|
||||
)];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
let (stability, span) = self.stability?;
|
||||
|
|
@ -120,6 +137,7 @@ impl<S: Stage> SingleAttributeParser<S> for ConstStabilityIndirectParser {
|
|||
const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::ConstStabilityIndirect)
|
||||
|
|
@ -146,7 +164,7 @@ impl ConstStabilityParser {
|
|||
|
||||
impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[
|
||||
(&[sym::rustc_const_stable], |this, cx, args| {
|
||||
(&[sym::rustc_const_stable], template!(List: r#"feature = "name""#), |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
|
||||
if !this.check_duplicate(cx)
|
||||
|
|
@ -158,7 +176,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
|
|||
));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_const_unstable], |this, cx, args| {
|
||||
(&[sym::rustc_const_unstable], template!(List: r#"feature = "name""#), |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_unstability(cx, args)
|
||||
|
|
@ -169,7 +187,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
|
|||
));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_promotable], |this, cx, _| {
|
||||
(&[sym::rustc_promotable], template!(Word), |this, cx, _| {
|
||||
reject_outside_std!(cx);
|
||||
this.promotable = true;
|
||||
}),
|
||||
|
|
@ -199,12 +217,10 @@ fn insert_value_into_option_or_error<S: Stage>(
|
|||
cx: &AcceptContext<'_, '_, S>,
|
||||
param: &MetaItemParser<'_>,
|
||||
item: &mut Option<Symbol>,
|
||||
name: Ident,
|
||||
) -> Option<()> {
|
||||
if item.is_some() {
|
||||
cx.emit_err(session_diagnostics::MultipleItem {
|
||||
span: param.span(),
|
||||
item: param.path().to_string(),
|
||||
});
|
||||
cx.duplicate_key(name.span, name.name);
|
||||
None
|
||||
} else if let Some(v) = param.args().name_value()
|
||||
&& let Some(s) = v.value_as_str()
|
||||
|
|
@ -212,10 +228,7 @@ fn insert_value_into_option_or_error<S: Stage>(
|
|||
*item = Some(s);
|
||||
Some(())
|
||||
} else {
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem {
|
||||
span: param.span(),
|
||||
suggestion: None,
|
||||
});
|
||||
cx.expected_name_value(param.span(), Some(name.name));
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -241,9 +254,14 @@ pub(crate) fn parse_stability<S: Stage>(
|
|||
return None;
|
||||
};
|
||||
|
||||
match param.path().word_sym() {
|
||||
Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
Some(sym::since) => insert_value_into_option_or_error(cx, ¶m, &mut since)?,
|
||||
let word = param.path().word();
|
||||
match word.map(|i| i.name) {
|
||||
Some(sym::feature) => {
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())?
|
||||
}
|
||||
Some(sym::since) => {
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())?
|
||||
}
|
||||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
|
|
@ -310,11 +328,16 @@ pub(crate) fn parse_unstability<S: Stage>(
|
|||
return None;
|
||||
};
|
||||
|
||||
match param.path().word_sym() {
|
||||
Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
Some(sym::reason) => insert_value_into_option_or_error(cx, ¶m, &mut reason)?,
|
||||
let word = param.path().word();
|
||||
match word.map(|i| i.name) {
|
||||
Some(sym::feature) => {
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())?
|
||||
}
|
||||
Some(sym::reason) => {
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut reason, word.unwrap())?
|
||||
}
|
||||
Some(sym::issue) => {
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut issue)?;
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut issue, word.unwrap())?;
|
||||
|
||||
// These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item
|
||||
// is a name/value pair string literal.
|
||||
|
|
@ -344,9 +367,11 @@ pub(crate) fn parse_unstability<S: Stage>(
|
|||
is_soft = true;
|
||||
}
|
||||
Some(sym::implied_by) => {
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut implied_by)?
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut implied_by, word.unwrap())?
|
||||
}
|
||||
Some(sym::old_name) => {
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())?
|
||||
}
|
||||
Some(sym::old_name) => insert_value_into_option_or_error(cx, ¶m, &mut old_name)?,
|
||||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param.span(),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
|
|
@ -17,14 +18,23 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
|
|||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Custom(|cx, used, unused| {
|
||||
cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes");
|
||||
});
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(NameValueStr: "transparent|semitransparent|opaque");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
match args.name_value().and_then(|nv| nv.value_as_str()) {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
};
|
||||
match nv.value_as_str() {
|
||||
Some(sym::transparent) => Some(Transparency::Transparent),
|
||||
Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque),
|
||||
Some(sym::opaque) => Some(Transparency::Opaque),
|
||||
Some(other) => {
|
||||
cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`"));
|
||||
Some(_) => {
|
||||
cx.expected_specific_argument_strings(
|
||||
nv.value_span,
|
||||
vec!["transparent", "semitransparent", "opaque"],
|
||||
);
|
||||
None
|
||||
}
|
||||
None => None,
|
||||
|
|
|
|||
|
|
@ -5,32 +5,36 @@ use std::ops::{Deref, DerefMut};
|
|||
use std::sync::LazyLock;
|
||||
|
||||
use private::Sealed;
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::NodeId;
|
||||
use rustc_ast::{self as ast, MetaItemLit, NodeId};
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
|
||||
use rustc_errors::{DiagCtxtHandle, Diagnostic};
|
||||
use rustc_feature::Features;
|
||||
use rustc_feature::{AttributeTemplate, Features};
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
|
||||
use crate::attributes::codegen_attrs::{ColdParser, NoMangleParser, OptimizeParser};
|
||||
use crate::attributes::confusables::ConfusablesParser;
|
||||
use crate::attributes::deprecation::DeprecationParser;
|
||||
use crate::attributes::lint_helpers::AsPtrParser;
|
||||
use crate::attributes::repr::ReprParser;
|
||||
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
|
||||
use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser};
|
||||
use crate::attributes::must_use::MustUseParser;
|
||||
use crate::attributes::repr::{AlignParser, ReprParser};
|
||||
use crate::attributes::semantics::MayDangleParser;
|
||||
use crate::attributes::stability::{
|
||||
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
|
||||
};
|
||||
use crate::attributes::transparency::TransparencyParser;
|
||||
use crate::attributes::{AttributeParser as _, Combine, Single};
|
||||
use crate::parser::{ArgParser, MetaItemParser};
|
||||
use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
|
||||
|
||||
macro_rules! group_type {
|
||||
($stage: ty) => {
|
||||
LazyLock<(
|
||||
BTreeMap<&'static [Symbol], Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>>,
|
||||
BTreeMap<&'static [Symbol], Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>)>>,
|
||||
Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>>
|
||||
)>
|
||||
};
|
||||
|
|
@ -59,7 +63,7 @@ macro_rules! attribute_parsers {
|
|||
@[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
|
||||
) => {
|
||||
pub(crate) static $name: group_type!($ty) = LazyLock::new(|| {
|
||||
let mut accepts = BTreeMap::<_, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>>::new();
|
||||
let mut accepts = BTreeMap::<_, Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>)>>::new();
|
||||
let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new();
|
||||
$(
|
||||
{
|
||||
|
|
@ -67,13 +71,12 @@ macro_rules! attribute_parsers {
|
|||
static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
|
||||
};
|
||||
|
||||
for (k, v) in <$names>::ATTRIBUTES {
|
||||
let old = accepts.insert(*k, Box::new(|cx, args| {
|
||||
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
|
||||
accepts.entry(*path).or_default().push((*template, Box::new(|cx, args| {
|
||||
STATE_OBJECT.with_borrow_mut(|s| {
|
||||
v(s, cx, args)
|
||||
accept_fn(s, cx, args)
|
||||
})
|
||||
}));
|
||||
assert!(old.is_none());
|
||||
})));
|
||||
}
|
||||
|
||||
finalizes.push(Box::new(|cx| {
|
||||
|
|
@ -90,6 +93,7 @@ macro_rules! attribute_parsers {
|
|||
attribute_parsers!(
|
||||
pub(crate) static ATTRIBUTE_PARSERS = [
|
||||
// tidy-alphabetical-start
|
||||
AlignParser,
|
||||
BodyStabilityParser,
|
||||
ConfusablesParser,
|
||||
ConstStabilityParser,
|
||||
|
|
@ -104,8 +108,16 @@ attribute_parsers!(
|
|||
|
||||
// tidy-alphabetical-start
|
||||
Single<AsPtrParser>,
|
||||
Single<ColdParser>,
|
||||
Single<ConstStabilityIndirectParser>,
|
||||
Single<DeprecationParser>,
|
||||
Single<InlineParser>,
|
||||
Single<MayDangleParser>,
|
||||
Single<MustUseParser>,
|
||||
Single<NoMangleParser>,
|
||||
Single<OptimizeParser>,
|
||||
Single<PubTransparentParser>,
|
||||
Single<RustcForceInlineParser>,
|
||||
Single<TransparencyParser>,
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
|
|
@ -165,6 +177,14 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
|
|||
pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
|
||||
/// The span of the attribute currently being parsed
|
||||
pub(crate) attr_span: Span,
|
||||
|
||||
/// The expected structure of the attribute.
|
||||
///
|
||||
/// Used in reporting errors to give a hint to users what the attribute *should* look like.
|
||||
pub(crate) template: &'f AttributeTemplate,
|
||||
|
||||
/// The name of the attribute we're currently accepting.
|
||||
pub(crate) attr_path: AttrPath,
|
||||
}
|
||||
|
||||
impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
||||
|
|
@ -172,10 +192,143 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
S::emit_err(&self.sess, diag)
|
||||
}
|
||||
|
||||
/// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
|
||||
/// must be delayed until after HIR is built. This method will take care of the details of
|
||||
/// that.
|
||||
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
|
||||
let id = self.target_id;
|
||||
(self.emit_lint)(AttributeLint { id, span, kind: lint });
|
||||
}
|
||||
|
||||
pub(crate) fn unknown_key(
|
||||
&self,
|
||||
span: Span,
|
||||
found: String,
|
||||
options: &'static [&'static str],
|
||||
) -> ErrorGuaranteed {
|
||||
self.emit_err(UnknownMetaItem { span, item: found, expected: options })
|
||||
}
|
||||
|
||||
/// error that a string literal was expected.
|
||||
/// You can optionally give the literal you did find (which you found not to be a string literal)
|
||||
/// which can make better errors. For example, if the literal was a byte string it will suggest
|
||||
/// removing the `b` prefix.
|
||||
pub(crate) fn expected_string_literal(
|
||||
&self,
|
||||
span: Span,
|
||||
actual_literal: Option<&MetaItemLit>,
|
||||
) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedStringLiteral {
|
||||
byte_string: actual_literal.and_then(|i| {
|
||||
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedList,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span: args_span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedNoArgs,
|
||||
})
|
||||
}
|
||||
|
||||
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
|
||||
/// a nicer error message talking about the specific name that was found lacking a value.
|
||||
pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedNameValue(name),
|
||||
})
|
||||
}
|
||||
|
||||
/// emit an error that a `name = value` pair was found where that name was already seen.
|
||||
pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::DuplicateKey(key),
|
||||
})
|
||||
}
|
||||
|
||||
/// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser)
|
||||
/// was expected *not* to be a literal, but instead a meta item.
|
||||
pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::UnexpectedLiteral,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedSingleArgument,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn expected_specific_argument(
|
||||
&self,
|
||||
span: Span,
|
||||
possibilities: Vec<&'static str>,
|
||||
) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
|
||||
possibilities,
|
||||
strings: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn expected_specific_argument_strings(
|
||||
&self,
|
||||
span: Span,
|
||||
possibilities: Vec<&'static str>,
|
||||
) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
|
||||
possibilities,
|
||||
strings: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
|
||||
|
|
@ -286,19 +439,13 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
|
||||
parsed.pop()
|
||||
}
|
||||
|
||||
pub fn new_early(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sess> AttributeParser<'sess, Late> {
|
||||
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
|
||||
}
|
||||
|
||||
pub(crate) fn sess(&self) -> &'sess Session {
|
||||
&self.sess
|
||||
}
|
||||
|
|
@ -374,18 +521,22 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
let args = parser.args();
|
||||
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accept) = S::parsers().0.get(parts.as_slice()) {
|
||||
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
|
||||
finalize_cx: FinalizeContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
attr_span: lower_span(attr.span),
|
||||
};
|
||||
if let Some(accepts) = S::parsers().0.get(parts.as_slice()) {
|
||||
for (template, accept) in accepts {
|
||||
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
|
||||
finalize_cx: FinalizeContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
attr_span: lower_span(attr.span),
|
||||
template,
|
||||
attr_path: path.get_attribute_path(),
|
||||
};
|
||||
|
||||
accept(&mut cx, args)
|
||||
accept(&mut cx, args)
|
||||
}
|
||||
} else {
|
||||
// If we're here, we must be compiling a tool attribute... Or someone
|
||||
// forgot to parse their fancy new attribute. Let's warn them in any case.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
|
||||
use rustc_errors::LintEmitter;
|
||||
use rustc_errors::{DiagArgValue, LintEmitter};
|
||||
use rustc_hir::HirId;
|
||||
|
||||
use crate::session_diagnostics;
|
||||
|
|
@ -15,5 +15,18 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
|
|||
*span,
|
||||
session_diagnostics::UnusedDuplicate { this, other, warning },
|
||||
),
|
||||
AttributeLintKind::IllFormedAttributeInput { suggestions } => {
|
||||
lint_emitter.emit_node_span_lint(
|
||||
rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT,
|
||||
*id,
|
||||
*span,
|
||||
session_diagnostics::IllFormedAttributeInput {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ use std::num::IntErrorKind;
|
|||
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
|
||||
use rustc_errors::{
|
||||
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
|
||||
};
|
||||
use rustc_feature::AttributeTemplate;
|
||||
use rustc_hir::AttrPath;
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
|
|
@ -12,8 +16,6 @@ pub(crate) enum UnsupportedLiteralReason {
|
|||
Generic,
|
||||
CfgString,
|
||||
CfgBoolean,
|
||||
DeprecatedString,
|
||||
DeprecatedKvPair,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -32,37 +34,6 @@ pub(crate) struct InvalidPredicate {
|
|||
pub predicate: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_multiple_item, code = E0538)]
|
||||
pub(crate) struct MultipleItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
||||
pub item: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_incorrect_meta_item, code = E0539)]
|
||||
pub(crate) struct IncorrectMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
||||
#[subdiagnostic]
|
||||
pub suggestion: Option<IncorrectMetaItemSuggestion>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
attr_parsing_incorrect_meta_item_suggestion,
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub(crate) struct IncorrectMetaItemSuggestion {
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub lo: Span,
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
/// Error code: E0541
|
||||
pub(crate) struct UnknownMetaItem<'a> {
|
||||
pub span: Span,
|
||||
|
|
@ -217,6 +188,7 @@ pub(crate) struct InvalidReprHintNoValue {
|
|||
}
|
||||
|
||||
/// Error code: E0565
|
||||
// FIXME(jdonszelmann): slowly phased out
|
||||
pub(crate) struct UnsupportedLiteral {
|
||||
pub span: Span,
|
||||
pub reason: UnsupportedLiteralReason,
|
||||
|
|
@ -239,12 +211,6 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
|
|||
UnsupportedLiteralReason::CfgBoolean => {
|
||||
fluent::attr_parsing_unsupported_literal_cfg_boolean
|
||||
}
|
||||
UnsupportedLiteralReason::DeprecatedString => {
|
||||
fluent::attr_parsing_unsupported_literal_deprecated_string
|
||||
}
|
||||
UnsupportedLiteralReason::DeprecatedKvPair => {
|
||||
fluent::attr_parsing_unsupported_literal_deprecated_kv_pair
|
||||
}
|
||||
},
|
||||
);
|
||||
diag.span(self.span);
|
||||
|
|
@ -462,6 +428,23 @@ pub(crate) struct UnusedDuplicate {
|
|||
pub warning: bool,
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): duplicated in rustc_lints, should be moved here completely.
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_ill_formed_attribute_input)]
|
||||
pub(crate) struct IllFormedAttributeInput {
|
||||
pub num_suggestions: usize,
|
||||
pub suggestions: DiagArgValue,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_ill_formed_attribute_input)]
|
||||
pub(crate) struct MustUseIllFormedAttributeInput {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub num_suggestions: usize,
|
||||
pub suggestions: DiagArgValue,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_stability_outside_std, code = E0734)]
|
||||
pub(crate) struct StabilityOutsideStd {
|
||||
|
|
@ -476,6 +459,14 @@ pub(crate) struct EmptyConfusables {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_invalid_alignment_value, code = E0589)]
|
||||
pub(crate) struct InvalidAlignmentValue {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub error_part: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_repr_ident, code = E0565)]
|
||||
pub(crate) struct ReprIdent {
|
||||
|
|
@ -490,3 +481,120 @@ pub(crate) struct UnrecognizedReprHint {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
pub(crate) enum AttributeParseErrorReason {
|
||||
ExpectedNoArgs,
|
||||
ExpectedStringLiteral { byte_string: Option<Span> },
|
||||
ExpectedSingleArgument,
|
||||
ExpectedList,
|
||||
UnexpectedLiteral,
|
||||
ExpectedNameValue(Option<Symbol>),
|
||||
DuplicateKey(Symbol),
|
||||
ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
|
||||
}
|
||||
|
||||
pub(crate) struct AttributeParseError {
|
||||
pub(crate) span: Span,
|
||||
pub(crate) attr_span: Span,
|
||||
pub(crate) template: AttributeTemplate,
|
||||
pub(crate) attribute: AttrPath,
|
||||
pub(crate) reason: AttributeParseErrorReason,
|
||||
}
|
||||
|
||||
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
|
||||
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
|
||||
let name = self.attribute.to_string();
|
||||
|
||||
let mut diag = Diag::new(dcx, level, format!("malformed `{name}` attribute input"));
|
||||
diag.span(self.attr_span);
|
||||
diag.code(E0539);
|
||||
match self.reason {
|
||||
AttributeParseErrorReason::ExpectedStringLiteral { byte_string } => {
|
||||
if let Some(start_point_span) = byte_string {
|
||||
diag.span_suggestion(
|
||||
start_point_span,
|
||||
fluent::attr_parsing_unsupported_literal_suggestion,
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.note("expected a normal string literal, not a byte string literal");
|
||||
|
||||
return diag;
|
||||
} else {
|
||||
diag.span_label(self.span, "expected a string literal here");
|
||||
}
|
||||
}
|
||||
AttributeParseErrorReason::ExpectedSingleArgument => {
|
||||
diag.span_label(self.span, "expected a single argument here");
|
||||
diag.code(E0805);
|
||||
}
|
||||
AttributeParseErrorReason::ExpectedList => {
|
||||
diag.span_label(self.span, "expected this to be a list");
|
||||
}
|
||||
AttributeParseErrorReason::DuplicateKey(key) => {
|
||||
diag.span_label(self.span, format!("found `{key}` used as a key more than once"));
|
||||
diag.code(E0538);
|
||||
}
|
||||
AttributeParseErrorReason::UnexpectedLiteral => {
|
||||
diag.span_label(self.span, format!("didn't expect a literal here"));
|
||||
diag.code(E0565);
|
||||
}
|
||||
AttributeParseErrorReason::ExpectedNoArgs => {
|
||||
diag.span_label(self.span, format!("didn't expect any arguments here"));
|
||||
diag.code(E0565);
|
||||
}
|
||||
AttributeParseErrorReason::ExpectedNameValue(None) => {
|
||||
diag.span_label(
|
||||
self.span,
|
||||
format!("expected this to be of the form `{name} = \"...\"`"),
|
||||
);
|
||||
}
|
||||
AttributeParseErrorReason::ExpectedNameValue(Some(name)) => {
|
||||
diag.span_label(
|
||||
self.span,
|
||||
format!("expected this to be of the form `{name} = \"...\"`"),
|
||||
);
|
||||
}
|
||||
AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => {
|
||||
let quote = if strings { '"' } else { '`' };
|
||||
match possibilities.as_slice() {
|
||||
&[] => {}
|
||||
&[x] => {
|
||||
diag.span_label(
|
||||
self.span,
|
||||
format!("the only valid argument here is {quote}{x}{quote}"),
|
||||
);
|
||||
}
|
||||
[first, second] => {
|
||||
diag.span_label(self.span, format!("valid arguments are {quote}{first}{quote} or {quote}{second}{quote}"));
|
||||
}
|
||||
[first @ .., second_to_last, last] => {
|
||||
let mut res = String::new();
|
||||
for i in first {
|
||||
res.push_str(&format!("{quote}{i}{quote}, "));
|
||||
}
|
||||
res.push_str(&format!(
|
||||
"{quote}{second_to_last}{quote} or {quote}{last}{quote}"
|
||||
));
|
||||
|
||||
diag.span_label(self.span, format!("valid arguments are {res}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let suggestions = self.template.suggestions(false, &name);
|
||||
diag.span_suggestions(
|
||||
self.attr_span,
|
||||
if suggestions.len() == 1 {
|
||||
"must be of the form"
|
||||
} else {
|
||||
"try changing it to one of the following valid forms of the attribute"
|
||||
},
|
||||
suggestions,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3201,14 +3201,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let expr_ty: Option<Ty<'_>> =
|
||||
visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
|
||||
|
||||
let is_format_arguments_item = if let Some(expr_ty) = expr_ty
|
||||
&& let ty::Adt(adt, _) = expr_ty.kind()
|
||||
{
|
||||
self.infcx.tcx.is_lang_item(adt.did(), LangItem::FormatArguments)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if visitor.found == 0
|
||||
&& stmt.span.contains(proper_span)
|
||||
&& let Some(p) = sm.span_to_margin(stmt.span)
|
||||
|
|
@ -3236,25 +3228,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
""
|
||||
};
|
||||
|
||||
if !is_format_arguments_item {
|
||||
let addition = format!(
|
||||
"let {}binding = {};\n{}",
|
||||
mutability,
|
||||
s,
|
||||
" ".repeat(p)
|
||||
);
|
||||
err.multipart_suggestion_verbose(
|
||||
msg,
|
||||
vec![
|
||||
(stmt.span.shrink_to_lo(), addition),
|
||||
(proper_span, "binding".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
err.note("the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used");
|
||||
err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
|
||||
}
|
||||
let addition =
|
||||
format!("let {}binding = {};\n{}", mutability, s, " ".repeat(p));
|
||||
err.multipart_suggestion_verbose(
|
||||
msg,
|
||||
vec![
|
||||
(stmt.span.shrink_to_lo(), addition),
|
||||
(proper_span, "binding".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
||||
suggested = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,8 @@ builtin_macros_concat_bytes_bad_repeat = repeat count is not a positive number
|
|||
builtin_macros_concat_bytes_invalid = cannot concatenate {$lit_kind} literals
|
||||
.byte_char = try using a byte character
|
||||
.byte_str = try using a byte string
|
||||
.c_str = try using a null-terminated byte string
|
||||
.c_str_note = concatenating C strings is ambiguous about including the '\0'
|
||||
.number_array = try wrapping the number in an array
|
||||
|
||||
builtin_macros_concat_bytes_missing_literal = expected a byte literal
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ pub(crate) fn expand(
|
|||
fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span) -> Stmt {
|
||||
let usize = cx.path_ident(span, Ident::new(sym::usize, span));
|
||||
let ty_usize = cx.ty_path(usize);
|
||||
let size = Ident::from_str_and_span("size", span);
|
||||
let align = Ident::from_str_and_span("align", span);
|
||||
let size = Ident::new(sym::size, span);
|
||||
let align = Ident::new(sym::align, span);
|
||||
|
||||
let layout_new = cx.std_path(&[sym::alloc, sym::Layout, sym::from_size_align_unchecked]);
|
||||
let layout_new = cx.expr_path(cx.path(span, layout_new));
|
||||
|
|
|
|||
|
|
@ -652,8 +652,10 @@ mod llvm_enzyme {
|
|||
exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]);
|
||||
} else {
|
||||
let q = QSelf { ty: d_ret_ty, path_span: span, position: 0 };
|
||||
let y =
|
||||
ExprKind::Path(Some(P(q)), ecx.path_ident(span, Ident::from_str("default")));
|
||||
let y = ExprKind::Path(
|
||||
Some(P(q)),
|
||||
ecx.path_ident(span, Ident::with_dummy_span(kw::Default)),
|
||||
);
|
||||
let default_call_expr = ecx.expr(span, y);
|
||||
let default_call_expr =
|
||||
ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ impl MutVisitor for CfgEval<'_> {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
fn visit_method_receiver_expr(&mut self, expr: &mut P<ast::Expr>) {
|
||||
fn visit_method_receiver_expr(&mut self, expr: &mut ast::Expr) {
|
||||
self.0.configure_expr(expr, true);
|
||||
mut_visit::walk_expr(self, expr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{ExprKind, LitIntType, LitKind, UintTy, token};
|
||||
use rustc_ast::{ExprKind, LitIntType, LitKind, StrStyle, UintTy, token};
|
||||
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
|
||||
use rustc_session::errors::report_lit_error;
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
|
|
@ -21,15 +21,32 @@ fn invalid_type_err(
|
|||
let snippet = cx.sess.source_map().span_to_snippet(span).ok();
|
||||
let dcx = cx.dcx();
|
||||
match LitKind::from_token_lit(token_lit) {
|
||||
Ok(LitKind::CStr(_, _)) => {
|
||||
Ok(LitKind::CStr(_, style)) => {
|
||||
// Avoid ambiguity in handling of terminal `NUL` by refusing to
|
||||
// concatenate C string literals as bytes.
|
||||
dcx.emit_err(errors::ConcatCStrLit { span })
|
||||
let sugg = if let Some(mut as_bstr) = snippet
|
||||
&& style == StrStyle::Cooked
|
||||
&& as_bstr.starts_with('c')
|
||||
&& as_bstr.ends_with('"')
|
||||
{
|
||||
// Suggest`c"foo"` -> `b"foo\0"` if we can
|
||||
as_bstr.replace_range(0..1, "b");
|
||||
as_bstr.pop();
|
||||
as_bstr.push_str(r#"\0""#);
|
||||
Some(ConcatBytesInvalidSuggestion::CStrLit { span, as_bstr })
|
||||
} else {
|
||||
// No suggestion for a missing snippet, raw strings, or if for some reason we have
|
||||
// a span that doesn't match `c"foo"` (possible if a proc macro assigns a span
|
||||
// that doesn't actually point to a C string).
|
||||
None
|
||||
};
|
||||
// We can only provide a suggestion if we have a snip and it is not a raw string
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "C string", sugg, cs_note: Some(()) })
|
||||
}
|
||||
Ok(LitKind::Char(_)) => {
|
||||
let sugg =
|
||||
snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg })
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg, cs_note: None })
|
||||
}
|
||||
Ok(LitKind::Str(_, _)) => {
|
||||
// suggestion would be invalid if we are nested
|
||||
|
|
@ -38,18 +55,21 @@ fn invalid_type_err(
|
|||
} else {
|
||||
None
|
||||
};
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg })
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg, cs_note: None })
|
||||
}
|
||||
Ok(LitKind::Float(_, _)) => {
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None })
|
||||
}
|
||||
Ok(LitKind::Bool(_)) => {
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "boolean", sugg: None })
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None, cs_note: None })
|
||||
}
|
||||
Ok(LitKind::Bool(_)) => dcx.emit_err(ConcatBytesInvalid {
|
||||
span,
|
||||
lit_kind: "boolean",
|
||||
sugg: None,
|
||||
cs_note: None,
|
||||
}),
|
||||
Ok(LitKind::Int(_, _)) if !is_nested => {
|
||||
let sugg =
|
||||
snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span, snippet });
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg })
|
||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg, cs_note: None })
|
||||
}
|
||||
Ok(LitKind::Int(val, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8))) => {
|
||||
assert!(val.get() > u8::MAX.into()); // must be an error
|
||||
|
|
|
|||
|
|
@ -215,6 +215,8 @@ pub(crate) struct ConcatBytesInvalid {
|
|||
pub(crate) lit_kind: &'static str,
|
||||
#[subdiagnostic]
|
||||
pub(crate) sugg: Option<ConcatBytesInvalidSuggestion>,
|
||||
#[note(builtin_macros_c_str_note)]
|
||||
pub(crate) cs_note: Option<()>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
|
@ -239,6 +241,13 @@ pub(crate) enum ConcatBytesInvalidSuggestion {
|
|||
span: Span,
|
||||
snippet: String,
|
||||
},
|
||||
#[note(builtin_macros_c_str_note)]
|
||||
#[suggestion(builtin_macros_c_str, code = "{as_bstr}", applicability = "machine-applicable")]
|
||||
CStrLit {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
as_bstr: String,
|
||||
},
|
||||
#[suggestion(
|
||||
builtin_macros_number_array,
|
||||
code = "[{snippet}]",
|
||||
|
|
|
|||
|
|
@ -606,6 +606,7 @@ fn make_format_args(
|
|||
template,
|
||||
arguments: args,
|
||||
uncooked_fmt_str,
|
||||
is_source_literal,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ pub fn inject(
|
|||
is_test_crate: bool,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
) {
|
||||
let ecfg = ExpansionConfig::default("proc_macro".to_string(), features);
|
||||
let ecfg = ExpansionConfig::default(sym::proc_macro, features);
|
||||
let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
|
||||
|
||||
let mut collect = CollectProcMacros {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub fn inject(
|
|||
let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id());
|
||||
let call_site = DUMMY_SP.with_call_site_ctxt(expn_id.to_expn_id());
|
||||
|
||||
let ecfg = ExpansionConfig::default("std_lib_injection".to_string(), features);
|
||||
let ecfg = ExpansionConfig::default(sym::std_lib_injection, features);
|
||||
let cx = ExtCtxt::new(sess, ecfg, resolver, None);
|
||||
|
||||
let ident_span = if edition >= Edition2018 { span } else { call_site };
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_ast as ast;
|
|||
use rustc_ast::entry::EntryPointType;
|
||||
use rustc_ast::mut_visit::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{Visitor, walk_item};
|
||||
use rustc_ast::visit::Visitor;
|
||||
use rustc_ast::{ModKind, attr};
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_expand::base::{ExtCtxt, ResolverExpand};
|
||||
|
|
@ -146,11 +146,11 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
|
|||
) = item.kind
|
||||
{
|
||||
let prev_tests = mem::take(&mut self.tests);
|
||||
walk_item_kind(&mut item.kind, item.span, item.id, &mut item.vis, (), self);
|
||||
ast::mut_visit::walk_item(self, item);
|
||||
self.add_test_cases(item.id, span, prev_tests);
|
||||
} else {
|
||||
// But in those cases, we emit a lint to warn the user of these missing tests.
|
||||
walk_item(&mut InnerItemLinter { sess: self.cx.ext_cx.sess }, &item);
|
||||
ast::visit::walk_item(&mut InnerItemLinter { sess: self.cx.ext_cx.sess }, &item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -227,7 +227,7 @@ fn generate_test_harness(
|
|||
panic_strategy: PanicStrategy,
|
||||
test_runner: Option<ast::Path>,
|
||||
) {
|
||||
let econfig = ExpansionConfig::default("test".to_string(), features);
|
||||
let econfig = ExpansionConfig::default(sym::test, features);
|
||||
let ext_cx = ExtCtxt::new(sess, econfig, resolver, None);
|
||||
|
||||
let expn_id = ext_cx.resolver.expansion_for_ast_pass(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Allocator shim
|
||||
// Adapted from rustc
|
||||
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use rustc_ast::expand::allocator::{
|
||||
ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
|
||||
alloc_error_handler_name, default_fn_name, global_fn_name,
|
||||
|
|
@ -97,16 +98,31 @@ fn codegen_inner(
|
|||
data.define(Box::new([val]));
|
||||
module.define_data(data_id, &data).unwrap();
|
||||
|
||||
let data_id = module
|
||||
.declare_data(
|
||||
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
|
||||
Linkage::Export,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let mut data = DataDescription::new();
|
||||
data.set_align(1);
|
||||
data.define(Box::new([0]));
|
||||
module.define_data(data_id, &data).unwrap();
|
||||
{
|
||||
let sig = Signature {
|
||||
call_conv: module.target_config().default_call_conv,
|
||||
params: vec![],
|
||||
returns: vec![],
|
||||
};
|
||||
let func_id = module
|
||||
.declare_function(
|
||||
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
|
||||
Linkage::Export,
|
||||
&sig,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut ctx = Context::new();
|
||||
ctx.func.signature = sig;
|
||||
let mut func_ctx = FunctionBuilderContext::new();
|
||||
let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
|
||||
|
||||
let block = bcx.create_block();
|
||||
bcx.switch_to_block(block);
|
||||
bcx.ins().return_(&[]);
|
||||
bcx.seal_all_blocks();
|
||||
bcx.finalize();
|
||||
|
||||
module.define_function(func_id, &mut ctx).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ impl CodegenBackend for CraneliftCodegenBackend {
|
|||
// FIXME return the actually used target features. this is necessary for #[cfg(target_feature)]
|
||||
let target_features = if sess.target.arch == "x86_64" && sess.target.os != "none" {
|
||||
// x86_64 mandates SSE2 support and rustc requires the x87 feature to be enabled
|
||||
vec![sym::fsxr, sym::sse, sym::sse2, Symbol::intern("x87")]
|
||||
vec![sym::fxsr, sym::sse, sym::sse2, Symbol::intern("x87")]
|
||||
} else if sess.target.arch == "aarch64" {
|
||||
match &*sess.target.os {
|
||||
"none" => vec![],
|
||||
|
|
|
|||
27
compiler/rustc_codegen_gcc/.cspell.json
Normal file
27
compiler/rustc_codegen_gcc/.cspell.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"allowCompoundWords": true,
|
||||
"dictionaries": ["cpp", "rust-extra", "rustc_codegen_gcc"],
|
||||
"dictionaryDefinitions": [
|
||||
{
|
||||
"name": "rust-extra",
|
||||
"path": "tools/cspell_dicts/rust.txt",
|
||||
"addWords": true
|
||||
},
|
||||
{
|
||||
"name": "rustc_codegen_gcc",
|
||||
"path": "tools/cspell_dicts/rustc_codegen_gcc.txt",
|
||||
"addWords": true
|
||||
}
|
||||
],
|
||||
"files": [
|
||||
"src/**/*.rs"
|
||||
],
|
||||
"ignorePaths": [
|
||||
"src/intrinsic/archs.rs",
|
||||
"src/intrinsic/llvm.rs"
|
||||
],
|
||||
"ignoreRegExpList": [
|
||||
"/(FIXME|NOTE|TODO)\\([^)]+\\)/",
|
||||
"__builtin_\\w*"
|
||||
]
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ permissions:
|
|||
env:
|
||||
# Enable backtraces for easier debugging
|
||||
RUST_BACKTRACE: 1
|
||||
# For the run-make tests.
|
||||
LLVM_BIN_DIR: /usr/bin
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
@ -48,7 +50,7 @@ jobs:
|
|||
|
||||
- name: Install packages
|
||||
# `llvm-14-tools` is needed to install the `FileCheck` binary which is used for asm tests.
|
||||
run: sudo apt-get install ninja-build ripgrep llvm-14-tools
|
||||
run: sudo apt-get install ninja-build ripgrep llvm-14-tools llvm
|
||||
|
||||
- name: Install rustfmt & clippy
|
||||
run: rustup component add rustfmt clippy
|
||||
|
|
@ -61,11 +63,15 @@ jobs:
|
|||
sudo dpkg --force-overwrite -i ${{ matrix.libgccjit_version.gcc }}
|
||||
echo 'gcc-path = "/usr/lib/"' > config.toml
|
||||
|
||||
# Some run-make tests fail if we use our forked GCC because it doesn't
|
||||
# bundle libstdc++, so we switch to gcc-14 to have a GCC that has
|
||||
# libstdc++.
|
||||
- name: Set default GCC to gcc-14
|
||||
run: sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 30
|
||||
|
||||
- name: Set env
|
||||
run: |
|
||||
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
|
||||
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
|
||||
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
|
||||
|
||||
#- name: Cache rust repository
|
||||
## We only clone the rust repository for rustc tests
|
||||
|
|
@ -76,12 +82,22 @@ jobs:
|
|||
#path: rust
|
||||
#key: ${{ runner.os }}-packages-${{ hashFiles('rust/.git/HEAD') }}
|
||||
|
||||
- name: Prepare
|
||||
run: ./y.sh prepare --only-libcore
|
||||
|
||||
- name: Check formatting
|
||||
run: ./y.sh fmt --check
|
||||
|
||||
- name: clippy
|
||||
run: |
|
||||
cargo clippy --all-targets -- -D warnings
|
||||
cargo clippy --all-targets --no-default-features -- -D warnings
|
||||
cargo clippy --manifest-path build_system/Cargo.toml --all-targets -- -D warnings
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./y.sh prepare --only-libcore
|
||||
./y.sh build --sysroot
|
||||
./y.sh test --mini-tests
|
||||
cargo test
|
||||
./y.sh test --cargo-tests
|
||||
|
||||
- name: Run y.sh cargo build
|
||||
run: |
|
||||
|
|
@ -101,20 +117,19 @@ jobs:
|
|||
run: |
|
||||
./y.sh test --release --clean --build-sysroot ${{ matrix.commands }}
|
||||
|
||||
- name: Check formatting
|
||||
run: ./y.sh fmt --check
|
||||
|
||||
- name: clippy
|
||||
run: |
|
||||
cargo clippy --all-targets -- -D warnings
|
||||
cargo clippy --all-targets --features master -- -D warnings
|
||||
|
||||
duplicates:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: python tools/check_intrinsics_duplicates.py
|
||||
|
||||
spell_check:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: crate-ci/typos@v1.32.0
|
||||
- uses: streetsidesoftware/cspell-action@v7
|
||||
|
||||
build_system:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ jobs:
|
|||
run: |
|
||||
sudo dpkg --force-overwrite -i gcc-15.deb
|
||||
echo 'gcc-path = "/usr/lib"' > config.toml
|
||||
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
|
||||
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
|
||||
|
||||
|
||||
|
||||
- name: Set env
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ jobs:
|
|||
- name: Set env
|
||||
run: |
|
||||
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
|
||||
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
|
||||
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
|
||||
|
||||
|
||||
|
||||
#- name: Cache rust repository
|
||||
## We only clone the rust repository for rustc tests
|
||||
|
|
@ -95,7 +95,7 @@ jobs:
|
|||
./y.sh prepare --only-libcore --cross
|
||||
./y.sh build --sysroot --features compiler_builtins/no-f16-f128 --target-triple m68k-unknown-linux-gnu
|
||||
./y.sh test --mini-tests
|
||||
CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu cargo test
|
||||
CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu ./y.sh test --cargo-tests
|
||||
./y.sh clean all
|
||||
|
||||
- name: Prepare dependencies
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ permissions:
|
|||
env:
|
||||
# Enable backtraces for easier debugging
|
||||
RUST_BACKTRACE: 1
|
||||
# For the run-make tests.
|
||||
LLVM_BIN_DIR: /usr/bin
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
@ -36,7 +38,8 @@ jobs:
|
|||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Install packages
|
||||
run: sudo apt-get install ninja-build ripgrep
|
||||
# `llvm-14-tools` is needed to install the `FileCheck` binary which is used for run-make tests.
|
||||
run: sudo apt-get install ninja-build ripgrep llvm-14-tools llvm
|
||||
|
||||
- name: Download artifact
|
||||
run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-15.deb
|
||||
|
|
@ -46,18 +49,21 @@ jobs:
|
|||
sudo dpkg --force-overwrite -i gcc-15.deb
|
||||
echo 'gcc-path = "/usr/lib/"' > config.toml
|
||||
|
||||
# Some run-make tests fail if we use our forked GCC because it doesn't
|
||||
# bundle libstdc++, so we switch to gcc-14 to have a GCC that has
|
||||
# libstdc++.
|
||||
- name: Set default GCC to gcc-14
|
||||
run: sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 30
|
||||
|
||||
- name: Set env
|
||||
run: |
|
||||
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
|
||||
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
|
||||
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./y.sh prepare --only-libcore
|
||||
EMBED_LTO_BITCODE=1 ./y.sh build --sysroot --release --release-sysroot
|
||||
./y.sh test --mini-tests
|
||||
cargo test
|
||||
./y.sh test --cargo-tests
|
||||
./y.sh clean all
|
||||
|
||||
- name: Prepare dependencies
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ jobs:
|
|||
if: ${{ !matrix.cargo_runner }}
|
||||
run: |
|
||||
./y.sh test --release --clean --release-sysroot --build-sysroot --mini-tests --std-tests --test-libcore
|
||||
cargo test
|
||||
./y.sh test --cargo-tests
|
||||
|
||||
- name: Run stdarch tests
|
||||
if: ${{ !matrix.cargo_runner }}
|
||||
|
|
|
|||
3
compiler/rustc_codegen_gcc/.gitignore
vendored
3
compiler/rustc_codegen_gcc/.gitignore
vendored
|
|
@ -19,4 +19,5 @@ tools/llvmint-2
|
|||
llvm
|
||||
build_system/target
|
||||
config.toml
|
||||
build
|
||||
build
|
||||
rustlantis
|
||||
|
|
@ -33,7 +33,7 @@ To run specific tests, use appropriate flags such as:
|
|||
|
||||
- `./y.sh test --test-libcore`
|
||||
- `./y.sh test --std-tests`
|
||||
- `cargo test -- <name of test>`
|
||||
- `./y.sh test --cargo-tests -- <name of test>`
|
||||
|
||||
Additionally, you can run the tests of `libgccjit`:
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,18 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
|
|
@ -111,9 +123,9 @@ checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
|||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
|
|
@ -137,6 +149,12 @@ version = "1.20.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.4"
|
||||
|
|
@ -166,9 +184,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.42"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
|
|
@ -188,12 +206,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.14.0"
|
||||
version = "3.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
|
|
@ -242,6 +260,15 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
@ -345,3 +372,12 @@ name = "windows_x86_64_msvc"
|
|||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ gccjit = "2.7"
|
|||
[dev-dependencies]
|
||||
boml = "0.3.1"
|
||||
lang_tester = "0.8.0"
|
||||
tempfile = "3.7.1"
|
||||
tempfile = "3.20"
|
||||
|
||||
[profile.dev]
|
||||
# By compiling dependencies with optimizations, performing tests gets much faster.
|
||||
|
|
|
|||
9
compiler/rustc_codegen_gcc/_typos.toml
Normal file
9
compiler/rustc_codegen_gcc/_typos.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[default.extend-words]
|
||||
ba = "ba"
|
||||
hsa = "hsa"
|
||||
olt = "olt"
|
||||
seh = "seh"
|
||||
typ = "typ"
|
||||
|
||||
[files]
|
||||
extend-exclude = ["src/intrinsic/archs.rs"]
|
||||
|
|
@ -1,24 +1,24 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.22.0"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"gimli 0.29.0",
|
||||
"gimli",
|
||||
"rustc-std-workspace-alloc",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
@ -33,10 +33,21 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
name = "alloctests"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"rand_xorshift",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
|
@ -50,10 +61,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.118"
|
||||
version = "0.1.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92afe7344b64cccf3662ca26d5d1c0828ab826f04206b97d856e3625e390e4b5"
|
||||
checksum = "6376049cfa92c0aa8b9ac95fae22184b981c658208d4ed8a1dc553cd83612895"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
|
|
@ -61,11 +73,19 @@ dependencies = [
|
|||
name = "core"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "coretests"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"rand_xorshift",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlmalloc"
|
||||
version = "0.2.6"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3264b043b8e977326c1ee9e723da2c1f8d09a99df52cacf00b4dbce5ac54414d"
|
||||
checksum = "8cff88b751e7a276c4ab0e222c3f355190adc6dde9ce39c851db39da34990df7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"compiler_builtins",
|
||||
|
|
@ -97,20 +117,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
|
|
@ -119,11 +128,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
@ -131,9 +139,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.4.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
|
||||
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
|
|
@ -142,9 +150,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
dependencies = [
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
|
@ -161,11 +169,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.4"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"adler2",
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
@ -173,9 +181,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.3"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
|
||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"memchr",
|
||||
|
|
@ -188,7 +196,6 @@ name = "panic_abort"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"alloc",
|
||||
"cfg-if",
|
||||
"compiler_builtins",
|
||||
"core",
|
||||
"libc",
|
||||
|
|
@ -211,14 +218,22 @@ name = "proc_macro"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
"rustc-literal-escaper",
|
||||
"std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profiler_builtins"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "4.5.0"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
@ -226,15 +241,39 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "r-efi-alloc"
|
||||
version = "1.0.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7"
|
||||
checksum = "e43c53ff1a01d423d1cb762fd991de07d32965ff0ca2e4f80444ac7804198203"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"r-efi",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
|
|
@ -245,6 +284,15 @@ dependencies = [
|
|||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-literal-escaper"
|
||||
version = "0.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
|
||||
dependencies = [
|
||||
"rustc-std-workspace-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-std-workspace-alloc"
|
||||
version = "1.99.0"
|
||||
|
|
@ -266,6 +314,12 @@ dependencies = [
|
|||
"std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "std"
|
||||
version = "0.0.0"
|
||||
|
|
@ -286,10 +340,13 @@ dependencies = [
|
|||
"panic_unwind",
|
||||
"r-efi",
|
||||
"r-efi-alloc",
|
||||
"rand",
|
||||
"rand_xorshift",
|
||||
"rustc-demangle",
|
||||
"std_detect",
|
||||
"unwind",
|
||||
"wasi",
|
||||
"windows-targets 0.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -298,6 +355,7 @@ version = "0.1.5"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"compiler_builtins",
|
||||
"libc",
|
||||
"rustc-std-workspace-alloc",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
|
@ -306,10 +364,8 @@ dependencies = [
|
|||
name = "sysroot"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"alloc",
|
||||
"compiler_builtins",
|
||||
"core",
|
||||
"proc_macro",
|
||||
"profiler_builtins",
|
||||
"std",
|
||||
"test",
|
||||
]
|
||||
|
|
@ -326,9 +382,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.13"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
@ -348,12 +404,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unwinding"
|
||||
version = "0.2.2"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc55842d0db6329a669d55a623c674b02d677b16bfb2d24857d4089d41eba882"
|
||||
checksum = "8393f2782b6060a807337ff353780c1ca15206f9ba2424df18cb6e733bd7b345"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"gimli 0.30.0",
|
||||
"gimli",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
|
|
@ -370,13 +426,17 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ impl BuildArg {
|
|||
}
|
||||
arg => {
|
||||
if !build_arg.config_info.parse_argument(arg, &mut args)? {
|
||||
return Err(format!("Unknown argument `{}`", arg));
|
||||
return Err(format!("Unknown argument `{arg}`"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,14 +105,14 @@ pub fn create_build_sysroot_content(start_dir: &Path) -> Result<(), String> {
|
|||
if !start_dir.is_dir() {
|
||||
create_dir(start_dir)?;
|
||||
}
|
||||
copy_file("build_system/build_sysroot/Cargo.toml", &start_dir.join("Cargo.toml"))?;
|
||||
copy_file("build_system/build_sysroot/Cargo.lock", &start_dir.join("Cargo.lock"))?;
|
||||
copy_file("build_system/build_sysroot/Cargo.toml", start_dir.join("Cargo.toml"))?;
|
||||
copy_file("build_system/build_sysroot/Cargo.lock", start_dir.join("Cargo.lock"))?;
|
||||
|
||||
let src_dir = start_dir.join("src");
|
||||
if !src_dir.is_dir() {
|
||||
create_dir(&src_dir)?;
|
||||
}
|
||||
copy_file("build_system/build_sysroot/lib.rs", &start_dir.join("src/lib.rs"))
|
||||
copy_file("build_system/build_sysroot/lib.rs", start_dir.join("src/lib.rs"))
|
||||
}
|
||||
|
||||
pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Result<(), String> {
|
||||
|
|
@ -169,7 +169,7 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
|
|||
run_command(&[&"cp", &"-r", &dir_to_copy, &sysroot_path], None).map(|_| ())
|
||||
};
|
||||
walk_dir(
|
||||
start_dir.join(&format!("target/{}/{}/deps", config.target_triple, channel)),
|
||||
start_dir.join(format!("target/{}/{}/deps", config.target_triple, channel)),
|
||||
&mut copier.clone(),
|
||||
&mut copier,
|
||||
false,
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ enum CleanArg {
|
|||
impl CleanArg {
|
||||
fn new() -> Result<Self, String> {
|
||||
// We skip the binary and the "clean" option.
|
||||
for arg in std::env::args().skip(2) {
|
||||
if let Some(arg) = std::env::args().nth(2) {
|
||||
return match arg.as_str() {
|
||||
"all" => Ok(Self::All),
|
||||
"ui-tests" => Ok(Self::UiTests),
|
||||
"--help" => Ok(Self::Help),
|
||||
a => Err(format!("Unknown argument `{}`", a)),
|
||||
a => Err(format!("Unknown argument `{a}`")),
|
||||
};
|
||||
}
|
||||
Ok(Self::default())
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ impl Args {
|
|||
}
|
||||
arg => {
|
||||
if !command_args.config_info.parse_argument(arg, &mut args)? {
|
||||
return Err(format!("Unknown option {}", arg));
|
||||
return Err(format!("Unknown option {arg}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ impl Args {
|
|||
Some(p) => p.into(),
|
||||
None => PathBuf::from("./gcc"),
|
||||
};
|
||||
return Ok(Some(command_args));
|
||||
Ok(Some(command_args))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ pub fn run() -> Result<(), String> {
|
|||
let result = git_clone("https://github.com/rust-lang/gcc", Some(&args.out_path), false)?;
|
||||
if result.ran_clone {
|
||||
let gcc_commit = args.config_info.get_gcc_commit()?;
|
||||
println!("Checking out GCC commit `{}`...", gcc_commit);
|
||||
println!("Checking out GCC commit `{gcc_commit}`...");
|
||||
run_command_with_output(
|
||||
&[&"git", &"checkout", &gcc_commit],
|
||||
Some(Path::new(&result.repo_dir)),
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl ConfigFile {
|
|||
"Expected a boolean for `download-gccjit`",
|
||||
);
|
||||
}
|
||||
_ => return failed_config_parsing(config_file, &format!("Unknown key `{}`", key)),
|
||||
_ => return failed_config_parsing(config_file, &format!("Unknown key `{key}`")),
|
||||
}
|
||||
}
|
||||
match (config.gcc_path.as_mut(), config.download_gccjit) {
|
||||
|
|
@ -86,9 +86,7 @@ impl ConfigFile {
|
|||
let path = Path::new(gcc_path);
|
||||
*gcc_path = path
|
||||
.canonicalize()
|
||||
.map_err(|err| {
|
||||
format!("Failed to get absolute path of `{}`: {:?}", gcc_path, err)
|
||||
})?
|
||||
.map_err(|err| format!("Failed to get absolute path of `{gcc_path}`: {err:?}"))?
|
||||
.display()
|
||||
.to_string();
|
||||
}
|
||||
|
|
@ -175,7 +173,7 @@ impl ConfigInfo {
|
|||
"--sysroot-panic-abort" => self.sysroot_panic_abort = true,
|
||||
"--gcc-path" => match args.next() {
|
||||
Some(arg) if !arg.is_empty() => {
|
||||
self.gcc_path = Some(arg.into());
|
||||
self.gcc_path = Some(arg);
|
||||
}
|
||||
_ => {
|
||||
return Err("Expected a value after `--gcc-path`, found nothing".to_string());
|
||||
|
|
@ -244,7 +242,7 @@ impl ConfigInfo {
|
|||
let libgccjit_so = output_dir.join(libgccjit_so_name);
|
||||
if !libgccjit_so.is_file() && !self.no_download {
|
||||
// Download time!
|
||||
let tempfile_name = format!("{}.download", libgccjit_so_name);
|
||||
let tempfile_name = format!("{libgccjit_so_name}.download");
|
||||
let tempfile = output_dir.join(&tempfile_name);
|
||||
let is_in_ci = std::env::var("GITHUB_ACTIONS").is_ok();
|
||||
|
||||
|
|
@ -262,14 +260,14 @@ impl ConfigInfo {
|
|||
)
|
||||
})?;
|
||||
|
||||
println!("Downloaded libgccjit.so version {} successfully!", commit);
|
||||
println!("Downloaded libgccjit.so version {commit} successfully!");
|
||||
// We need to create a link named `libgccjit.so.0` because that's what the linker is
|
||||
// looking for.
|
||||
create_symlink(&libgccjit_so, output_dir.join(&format!("{}.0", libgccjit_so_name)))?;
|
||||
create_symlink(&libgccjit_so, output_dir.join(format!("{libgccjit_so_name}.0")))?;
|
||||
}
|
||||
|
||||
let gcc_path = output_dir.display().to_string();
|
||||
println!("Using `{}` as path for libgccjit", gcc_path);
|
||||
println!("Using `{gcc_path}` as path for libgccjit");
|
||||
self.gcc_path = Some(gcc_path);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -286,8 +284,7 @@ impl ConfigInfo {
|
|||
// since we already have everything we need.
|
||||
if let Some(gcc_path) = &self.gcc_path {
|
||||
println!(
|
||||
"`--gcc-path` was provided, ignoring config file. Using `{}` as path for libgccjit",
|
||||
gcc_path
|
||||
"`--gcc-path` was provided, ignoring config file. Using `{gcc_path}` as path for libgccjit"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -343,7 +340,7 @@ impl ConfigInfo {
|
|||
self.dylib_ext = match os_name.as_str() {
|
||||
"Linux" => "so",
|
||||
"Darwin" => "dylib",
|
||||
os => return Err(format!("unsupported OS `{}`", os)),
|
||||
os => return Err(format!("unsupported OS `{os}`")),
|
||||
}
|
||||
.to_string();
|
||||
let rustc = match env.get("RUSTC") {
|
||||
|
|
@ -355,10 +352,10 @@ impl ConfigInfo {
|
|||
None => return Err("no host found".to_string()),
|
||||
};
|
||||
|
||||
if self.target_triple.is_empty() {
|
||||
if let Some(overwrite) = env.get("OVERWRITE_TARGET_TRIPLE") {
|
||||
self.target_triple = overwrite.clone();
|
||||
}
|
||||
if self.target_triple.is_empty()
|
||||
&& let Some(overwrite) = env.get("OVERWRITE_TARGET_TRIPLE")
|
||||
{
|
||||
self.target_triple = overwrite.clone();
|
||||
}
|
||||
if self.target_triple.is_empty() {
|
||||
self.target_triple = self.host_triple.clone();
|
||||
|
|
@ -378,7 +375,7 @@ impl ConfigInfo {
|
|||
}
|
||||
|
||||
let current_dir =
|
||||
std_env::current_dir().map_err(|error| format!("`current_dir` failed: {:?}", error))?;
|
||||
std_env::current_dir().map_err(|error| format!("`current_dir` failed: {error:?}"))?;
|
||||
let channel = if self.channel == Channel::Release {
|
||||
"release"
|
||||
} else if let Some(channel) = env.get("CHANNEL") {
|
||||
|
|
@ -391,15 +388,15 @@ impl ConfigInfo {
|
|||
self.cg_backend_path = current_dir
|
||||
.join("target")
|
||||
.join(channel)
|
||||
.join(&format!("librustc_codegen_gcc.{}", self.dylib_ext))
|
||||
.join(format!("librustc_codegen_gcc.{}", self.dylib_ext))
|
||||
.display()
|
||||
.to_string();
|
||||
self.sysroot_path =
|
||||
current_dir.join(&get_sysroot_dir()).join("sysroot").display().to_string();
|
||||
current_dir.join(get_sysroot_dir()).join("sysroot").display().to_string();
|
||||
if let Some(backend) = &self.backend {
|
||||
// This option is only used in the rust compiler testsuite. The sysroot is handled
|
||||
// by its build system directly so no need to set it ourselves.
|
||||
rustflags.push(format!("-Zcodegen-backend={}", backend));
|
||||
rustflags.push(format!("-Zcodegen-backend={backend}"));
|
||||
} else {
|
||||
rustflags.extend_from_slice(&[
|
||||
"--sysroot".to_string(),
|
||||
|
|
@ -412,10 +409,10 @@ impl ConfigInfo {
|
|||
// We have a different environment variable than RUSTFLAGS to make sure those flags are
|
||||
// only sent to rustc_codegen_gcc and not the LLVM backend.
|
||||
if let Some(cg_rustflags) = env.get("CG_RUSTFLAGS") {
|
||||
rustflags.extend_from_slice(&split_args(&cg_rustflags)?);
|
||||
rustflags.extend_from_slice(&split_args(cg_rustflags)?);
|
||||
}
|
||||
if let Some(test_flags) = env.get("TEST_FLAGS") {
|
||||
rustflags.extend_from_slice(&split_args(&test_flags)?);
|
||||
rustflags.extend_from_slice(&split_args(test_flags)?);
|
||||
}
|
||||
|
||||
if let Some(linker) = linker {
|
||||
|
|
@ -438,8 +435,8 @@ impl ConfigInfo {
|
|||
env.insert("RUSTC_LOG".to_string(), "warn".to_string());
|
||||
|
||||
let sysroot = current_dir
|
||||
.join(&get_sysroot_dir())
|
||||
.join(&format!("sysroot/lib/rustlib/{}/lib", self.target_triple));
|
||||
.join(get_sysroot_dir())
|
||||
.join(format!("sysroot/lib/rustlib/{}/lib", self.target_triple));
|
||||
let ld_library_path = format!(
|
||||
"{target}:{sysroot}:{gcc_path}",
|
||||
target = self.cargo_target_dir,
|
||||
|
|
@ -505,7 +502,7 @@ fn download_gccjit(
|
|||
with_progress_bar: bool,
|
||||
) -> Result<(), String> {
|
||||
let url = if std::env::consts::OS == "linux" && std::env::consts::ARCH == "x86_64" {
|
||||
format!("https://github.com/rust-lang/gcc/releases/download/master-{}/libgccjit.so", commit)
|
||||
format!("https://github.com/rust-lang/gcc/releases/download/master-{commit}/libgccjit.so")
|
||||
} else {
|
||||
eprintln!(
|
||||
"\
|
||||
|
|
@ -518,7 +515,7 @@ to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
|
|||
));
|
||||
};
|
||||
|
||||
println!("Downloading `{}`...", url);
|
||||
println!("Downloading `{url}`...");
|
||||
|
||||
// Try curl. If that fails and we are on windows, fallback to PowerShell.
|
||||
let mut ret = run_command_with_output(
|
||||
|
|
@ -538,7 +535,7 @@ to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
|
|||
if with_progress_bar { &"--progress-bar" } else { &"-s" },
|
||||
&url.as_str(),
|
||||
],
|
||||
Some(&output_dir),
|
||||
Some(output_dir),
|
||||
);
|
||||
if ret.is_err() && cfg!(windows) {
|
||||
eprintln!("Fallback to PowerShell");
|
||||
|
|
@ -549,12 +546,11 @@ to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
|
|||
&"-Command",
|
||||
&"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
|
||||
&format!(
|
||||
"(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
|
||||
url, tempfile_name,
|
||||
"(New-Object System.Net.WebClient).DownloadFile('{url}', '{tempfile_name}')",
|
||||
)
|
||||
.as_str(),
|
||||
],
|
||||
Some(&output_dir),
|
||||
Some(output_dir),
|
||||
);
|
||||
}
|
||||
ret
|
||||
|
|
|
|||
|
|
@ -16,21 +16,21 @@ fn show_usage() {
|
|||
pub fn run() -> Result<(), String> {
|
||||
let mut check = false;
|
||||
// We skip binary name and the `info` command.
|
||||
let mut args = std::env::args().skip(2);
|
||||
while let Some(arg) = args.next() {
|
||||
let args = std::env::args().skip(2);
|
||||
for arg in args {
|
||||
match arg.as_str() {
|
||||
"--help" => {
|
||||
show_usage();
|
||||
return Ok(());
|
||||
}
|
||||
"--check" => check = true,
|
||||
_ => return Err(format!("Unknown option {}", arg)),
|
||||
_ => return Err(format!("Unknown option {arg}")),
|
||||
}
|
||||
}
|
||||
|
||||
let cmd: &[&dyn AsRef<OsStr>] =
|
||||
if check { &[&"cargo", &"fmt", &"--check"] } else { &[&"cargo", &"fmt"] };
|
||||
|
||||
run_command_with_output(cmd, Some(&Path::new(".")))?;
|
||||
run_command_with_output(cmd, Some(&Path::new("build_system")))
|
||||
run_command_with_output(cmd, Some(Path::new(".")))?;
|
||||
run_command_with_output(cmd, Some(Path::new("build_system")))
|
||||
}
|
||||
|
|
|
|||
289
compiler/rustc_codegen_gcc/build_system/src/fuzz.rs
Normal file
289
compiler/rustc_codegen_gcc/build_system/src/fuzz.rs
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
|
||||
mod reduce;
|
||||
|
||||
use crate::utils::run_command_with_output;
|
||||
|
||||
fn show_usage() {
|
||||
println!(
|
||||
r#"
|
||||
`fuzz` command help:
|
||||
--reduce : Reduces a file generated by rustlantis
|
||||
--help : Show this help
|
||||
--start : Start of the fuzzed range
|
||||
--count : The number of cases to fuzz
|
||||
-j --jobs : The number of threads to use during fuzzing"#
|
||||
);
|
||||
}
|
||||
|
||||
pub fn run() -> Result<(), String> {
|
||||
// We skip binary name and the `fuzz` command.
|
||||
let mut args = std::env::args().skip(2);
|
||||
let mut start = 0;
|
||||
let mut count = 100;
|
||||
let mut threads =
|
||||
std::thread::available_parallelism().map(|threads| threads.get()).unwrap_or(1);
|
||||
while let Some(arg) = args.next() {
|
||||
match arg.as_str() {
|
||||
"--reduce" => {
|
||||
let Some(path) = args.next() else {
|
||||
return Err("--reduce must be provided with a path".into());
|
||||
};
|
||||
if !std::fs::exists(&path).unwrap_or(false) {
|
||||
return Err("--reduce must be provided with a valid path".into());
|
||||
}
|
||||
reduce::reduce(&path);
|
||||
return Ok(());
|
||||
}
|
||||
"--help" => {
|
||||
show_usage();
|
||||
return Ok(());
|
||||
}
|
||||
"--start" => {
|
||||
start =
|
||||
str::parse(&args.next().ok_or_else(|| "Fuzz start not provided!".to_string())?)
|
||||
.map_err(|err| (format!("Fuzz start not a number {err:?}!")))?;
|
||||
}
|
||||
"--count" => {
|
||||
count =
|
||||
str::parse(&args.next().ok_or_else(|| "Fuzz count not provided!".to_string())?)
|
||||
.map_err(|err| (format!("Fuzz count not a number {err:?}!")))?;
|
||||
}
|
||||
"-j" | "--jobs" => {
|
||||
threads = str::parse(
|
||||
&args.next().ok_or_else(|| "Fuzz thread count not provided!".to_string())?,
|
||||
)
|
||||
.map_err(|err| (format!("Fuzz thread count not a number {err:?}!")))?;
|
||||
}
|
||||
_ => return Err(format!("Unknown option {arg}")),
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we have a cloned version of rustlantis on hand.
|
||||
crate::utils::git_clone(
|
||||
"https://github.com/cbeuw/rustlantis.git",
|
||||
Some("clones/rustlantis".as_ref()),
|
||||
true,
|
||||
)
|
||||
.map_err(|err| (format!("Git clone failed with message: {err:?}!")))?;
|
||||
|
||||
// Ensure that we are on the newest rustlantis commit.
|
||||
let cmd: &[&dyn AsRef<OsStr>] = &[&"git", &"pull", &"origin"];
|
||||
run_command_with_output(cmd, Some(Path::new("clones/rustlantis")))?;
|
||||
|
||||
// Build the release version of rustlantis
|
||||
let cmd: &[&dyn AsRef<OsStr>] = &[&"cargo", &"build", &"--release"];
|
||||
run_command_with_output(cmd, Some(Path::new("clones/rustlantis")))?;
|
||||
// Fuzz a given range
|
||||
fuzz_range(start, start + count, threads);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fuzzes a range `start..end` with `threads`.
|
||||
fn fuzz_range(start: u64, end: u64, threads: usize) {
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::time::{Duration, Instant};
|
||||
// Total amount of files to fuzz
|
||||
let total = end - start;
|
||||
// Currently fuzzed element
|
||||
let start = Arc::new(AtomicU64::new(start));
|
||||
// Count time during fuzzing
|
||||
let start_time = Instant::now();
|
||||
let mut workers = Vec::with_capacity(threads);
|
||||
// Spawn `threads`..
|
||||
for _ in 0..threads {
|
||||
let start = start.clone();
|
||||
// .. which each will ..
|
||||
workers.push(std::thread::spawn(move || {
|
||||
// ... grab the next fuzz seed ...
|
||||
while start.load(Ordering::Relaxed) < end {
|
||||
let next = start.fetch_add(1, Ordering::Relaxed);
|
||||
// .. test that seed .
|
||||
match test(next, false) {
|
||||
Err(err) => {
|
||||
// If the test failed at compile-time...
|
||||
println!("test({next}) failed because {err:?}");
|
||||
// ... copy that file to the directory `target/fuzz/compiletime_error`...
|
||||
let mut out_path: std::path::PathBuf =
|
||||
"target/fuzz/compiletime_error".into();
|
||||
std::fs::create_dir_all(&out_path).unwrap();
|
||||
// .. into a file named `fuzz{seed}.rs`.
|
||||
out_path.push(format!("fuzz{next}.rs"));
|
||||
std::fs::copy(err, out_path).unwrap();
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
// If the test failed at run-time...
|
||||
println!("The LLVM and GCC results don't match for {err:?}");
|
||||
// ... generate a new file, which prints temporaries(instead of hashing them)...
|
||||
let mut out_path: std::path::PathBuf = "target/fuzz/runtime_error".into();
|
||||
std::fs::create_dir_all(&out_path).unwrap();
|
||||
let Ok(Err(tmp_print_err)) = test(next, true) else {
|
||||
// ... if that file does not reproduce the issue...
|
||||
// ... save the original sample in a file named `fuzz{seed}.rs`...
|
||||
out_path.push(format!("fuzz{next}.rs"));
|
||||
std::fs::copy(err, &out_path).unwrap();
|
||||
continue;
|
||||
};
|
||||
// ... if that new file still produces the issue, copy it to `fuzz{seed}.rs`..
|
||||
out_path.push(format!("fuzz{next}.rs"));
|
||||
std::fs::copy(tmp_print_err, &out_path).unwrap();
|
||||
// ... and start reducing it, using some properties of `rustlantis` to speed up the process.
|
||||
reduce::reduce(&out_path);
|
||||
}
|
||||
// If the test passed, do nothing
|
||||
Ok(Ok(())) => (),
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
// The "manager" thread loop.
|
||||
while start.load(Ordering::Relaxed) < end || !workers.iter().all(|t| t.is_finished()) {
|
||||
// Every 500 ms...
|
||||
let five_hundred_millis = Duration::from_millis(500);
|
||||
std::thread::sleep(five_hundred_millis);
|
||||
// ... calculate the remaining fuzz iters ...
|
||||
let remaining = end - start.load(Ordering::Relaxed);
|
||||
// ... fix the count(the start counter counts the cases that
|
||||
// begun fuzzing, and not only the ones that are done)...
|
||||
let fuzzed = (total - remaining).saturating_sub(threads as u64);
|
||||
// ... and the fuzz speed ...
|
||||
let iter_per_sec = fuzzed as f64 / start_time.elapsed().as_secs_f64();
|
||||
// .. and use them to display fuzzing stats.
|
||||
println!(
|
||||
"fuzzed {fuzzed} cases({}%), at rate {iter_per_sec} iter/s, remaining ~{}s",
|
||||
(100 * fuzzed) as f64 / total as f64,
|
||||
(remaining as f64) / iter_per_sec
|
||||
)
|
||||
}
|
||||
drop(workers);
|
||||
}
|
||||
|
||||
/// Builds & runs a file with LLVM.
|
||||
fn debug_llvm(path: &std::path::Path) -> Result<Vec<u8>, String> {
|
||||
// Build a file named `llvm_elf`...
|
||||
let exe_path = path.with_extension("llvm_elf");
|
||||
// ... using the LLVM backend ...
|
||||
let output = std::process::Command::new("rustc")
|
||||
.arg(path)
|
||||
.arg("-o")
|
||||
.arg(&exe_path)
|
||||
.output()
|
||||
.map_err(|err| format!("{err:?}"))?;
|
||||
// ... check that the compilation succeeded ...
|
||||
if !output.status.success() {
|
||||
return Err(format!("LLVM compilation failed:{output:?}"));
|
||||
}
|
||||
// ... run the resulting executable ...
|
||||
let output =
|
||||
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
|
||||
// ... check it run normally ...
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"The program at {path:?}, compiled with LLVM, exited unsuccessfully:{output:?}"
|
||||
));
|
||||
}
|
||||
// ... cleanup that executable ...
|
||||
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
|
||||
// ... and return the output(stdout + stderr - this allows UB checks to fire).
|
||||
let mut res = output.stdout;
|
||||
res.extend(output.stderr);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Builds & runs a file with GCC.
|
||||
fn release_gcc(path: &std::path::Path) -> Result<Vec<u8>, String> {
|
||||
// Build a file named `gcc_elf`...
|
||||
let exe_path = path.with_extension("gcc_elf");
|
||||
// ... using the GCC backend ...
|
||||
let output = std::process::Command::new("./y.sh")
|
||||
.arg("rustc")
|
||||
.arg(path)
|
||||
.arg("-O")
|
||||
.arg("-o")
|
||||
.arg(&exe_path)
|
||||
.output()
|
||||
.map_err(|err| format!("{err:?}"))?;
|
||||
// ... check that the compilation succeeded ...
|
||||
if !output.status.success() {
|
||||
return Err(format!("GCC compilation failed:{output:?}"));
|
||||
}
|
||||
// ... run the resulting executable ..
|
||||
let output =
|
||||
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
|
||||
// ... check it run normally ...
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"The program at {path:?}, compiled with GCC, exited unsuccessfully:{output:?}"
|
||||
));
|
||||
}
|
||||
// ... cleanup that executable ...
|
||||
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
|
||||
// ... and return the output(stdout + stderr - this allows UB checks to fire).
|
||||
let mut res = output.stdout;
|
||||
res.extend(output.stderr);
|
||||
Ok(res)
|
||||
}
|
||||
type ResultCache = Option<(Vec<u8>, Vec<u8>)>;
|
||||
/// Generates a new rustlantis file, & compares the result of running it with GCC and LLVM.
|
||||
fn test(seed: u64, print_tmp_vars: bool) -> Result<Result<(), std::path::PathBuf>, String> {
|
||||
// Generate a Rust source...
|
||||
let source_file = generate(seed, print_tmp_vars)?;
|
||||
test_file(&source_file, true)
|
||||
}
|
||||
/// Tests a file with a cached LLVM result. Used for reduction, when it is known
|
||||
/// that a given transformation should not change the execution result.
|
||||
fn test_cached(
|
||||
source_file: &Path,
|
||||
remove_tmps: bool,
|
||||
cache: &mut ResultCache,
|
||||
) -> Result<Result<(), std::path::PathBuf>, String> {
|
||||
// Test `source_file` with release GCC ...
|
||||
let gcc_res = release_gcc(source_file)?;
|
||||
if cache.is_none() {
|
||||
// ...test `source_file` with debug LLVM ...
|
||||
*cache = Some((debug_llvm(source_file)?, gcc_res.clone()));
|
||||
}
|
||||
let (llvm_res, old_gcc) = cache.as_ref().unwrap();
|
||||
// ... compare the results ...
|
||||
if *llvm_res != gcc_res && gcc_res == *old_gcc {
|
||||
// .. if they don't match, report an error.
|
||||
Ok(Err(source_file.to_path_buf()))
|
||||
} else {
|
||||
if remove_tmps {
|
||||
std::fs::remove_file(source_file).map_err(|err| format!("{err:?}"))?;
|
||||
}
|
||||
Ok(Ok(()))
|
||||
}
|
||||
}
|
||||
fn test_file(
|
||||
source_file: &Path,
|
||||
remove_tmps: bool,
|
||||
) -> Result<Result<(), std::path::PathBuf>, String> {
|
||||
let mut uncached = None;
|
||||
test_cached(source_file, remove_tmps, &mut uncached)
|
||||
}
|
||||
|
||||
/// Generates a new rustlantis file for us to run tests on.
|
||||
fn generate(seed: u64, print_tmp_vars: bool) -> Result<std::path::PathBuf, String> {
|
||||
use std::io::Write;
|
||||
let mut out_path = std::env::temp_dir();
|
||||
out_path.push(format!("fuzz{seed}.rs"));
|
||||
// We need to get the command output here.
|
||||
let mut generate = std::process::Command::new("cargo");
|
||||
generate
|
||||
.args(["run", "--release", "--bin", "generate"])
|
||||
.arg(format!("{seed}"))
|
||||
.current_dir("clones/rustlantis");
|
||||
if print_tmp_vars {
|
||||
generate.arg("--debug");
|
||||
}
|
||||
let out = generate.output().map_err(|err| format!("{err:?}"))?;
|
||||
// Stuff the rustlantis output in a source file.
|
||||
std::fs::File::create(&out_path)
|
||||
.map_err(|err| format!("{err:?}"))?
|
||||
.write_all(&out.stdout)
|
||||
.map_err(|err| format!("{err:?}"))?;
|
||||
Ok(out_path)
|
||||
}
|
||||
432
compiler/rustc_codegen_gcc/build_system/src/fuzz/reduce.rs
Normal file
432
compiler/rustc_codegen_gcc/build_system/src/fuzz/reduce.rs
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::ResultCache;
|
||||
|
||||
/// Saves a reduced file for a given `stage`
|
||||
fn save_reduction(lines: &[String], path: &Path, stage: &str) {
|
||||
let mut path = path.to_path_buf();
|
||||
path.set_extension(format!("rs.{stage}"));
|
||||
let mut file = std::fs::File::create(&path).expect("Could not create the reduced example file");
|
||||
for line in lines {
|
||||
file.write_all(line.as_bytes()).expect("Could not save the reduced example");
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a given reduction is valid.
|
||||
fn test_reduction(lines: &[String], path: &Path, cache: &mut ResultCache) -> bool {
|
||||
let mut path = path.to_path_buf();
|
||||
path.set_extension("rs_reduced");
|
||||
let mut file = std::fs::File::create(&path).expect("Could not create the reduced example file");
|
||||
for line in lines {
|
||||
file.write_all(line.as_bytes()).expect("Could not save the reduced example");
|
||||
}
|
||||
let res = super::test_cached(&path, false, cache);
|
||||
let Ok(Err(_)) = res else {
|
||||
return false;
|
||||
};
|
||||
true
|
||||
}
|
||||
|
||||
/// Removes duplicate assignments in bulk.
|
||||
/// If a line A = B is followed directly by A = C,
|
||||
/// then removing the first line ought to be fully sound,
|
||||
/// and not change the behaviour of the program at all. Detect & remove such lines.
|
||||
fn remove_dup_assign(
|
||||
file: &mut Vec<String>,
|
||||
path: &PathBuf,
|
||||
starts: usize,
|
||||
ends: usize,
|
||||
cache: &mut ResultCache,
|
||||
) {
|
||||
let mut file_copy = file.clone();
|
||||
let mut reduction_count = 0;
|
||||
// Not worth it.
|
||||
if ends - starts < 8 {
|
||||
return;
|
||||
}
|
||||
for index in starts..ends {
|
||||
let Some((prefix, _)) = file_copy[index].split_once('=') else {
|
||||
continue;
|
||||
};
|
||||
let Some((prefix2, postifx2)) = file_copy[index + 1].split_once('=') else {
|
||||
continue;
|
||||
};
|
||||
let prefix = prefix.trim();
|
||||
let prefix2 = prefix2.trim();
|
||||
// FIXME: Right now, remove_dup_assign cares about assignments to the exact same place.
|
||||
// However, given an assigemnt like this:
|
||||
// ```
|
||||
// A.0 = 1_u32;
|
||||
// A = (2_u32, 3.0);
|
||||
// ```
|
||||
// The first assignment could be safely omitted.
|
||||
// Additionally, we try to check if the second assignment could depend on the first one.
|
||||
// In such cases, the result is likely to change, so we bail.
|
||||
if prefix == prefix2 && !postifx2.contains(prefix) {
|
||||
file_copy[index] = "".into();
|
||||
reduction_count += 1;
|
||||
}
|
||||
}
|
||||
// We have removed no lines - no point in testing.
|
||||
if reduction_count == 0 {
|
||||
return;
|
||||
}
|
||||
// Check if the removed lines affected the execution result in any way, shape or form.
|
||||
if test_reduction(&file_copy, path, cache) {
|
||||
println!("Reduced {path:?} by {reduction_count} lines `remove_dup_assign`");
|
||||
*file = file_copy;
|
||||
} else {
|
||||
// The execution result changed.
|
||||
// This can occur if the second assignment depended on the first one.
|
||||
// Eg.
|
||||
// ```
|
||||
// a = b + c;
|
||||
// a = a + d;
|
||||
// ```
|
||||
remove_dup_assign(file, path, starts, (starts + ends) / 2, cache);
|
||||
remove_dup_assign(file, path, (starts + ends) / 2, ends, cache);
|
||||
}
|
||||
save_reduction(file, path, "remove_dup_assign");
|
||||
}
|
||||
|
||||
/// Removes all the unneeded calls to `dump_var`. This is not something tools like `cvise` can do,
|
||||
/// but it greately speeds up MIR interpretation + native execution.
|
||||
fn remove_dump_var(file: &mut Vec<String>, path: &PathBuf) {
|
||||
let mut curr = 0;
|
||||
// ... try disabling `dump_vars` one by one, until only the necessary ones are left.
|
||||
while curr < file.len() {
|
||||
let Some(line) = file[curr..].iter().position(|line| line.contains("dump_var")) else {
|
||||
// No more `dump_var`s to remove - exit early.
|
||||
break;
|
||||
};
|
||||
// Make the line absolute again.
|
||||
let line = line + curr;
|
||||
let mut file_copy = file.clone();
|
||||
// Try removing 3 consecutive lines(the call, block end and block beginning). This effectively removes a `dump_var`.
|
||||
file_copy.remove(line);
|
||||
file_copy.remove(line);
|
||||
file_copy.remove(line);
|
||||
// Not cached - the execution result can change.
|
||||
let mut uncached = None;
|
||||
// Check if this reduction is valid.
|
||||
if test_reduction(&file_copy, path, &mut uncached) {
|
||||
println!("Reduced {path:?} by 3 lines `remove_dump_var`");
|
||||
*file = file_copy;
|
||||
curr = line;
|
||||
} else {
|
||||
curr = line + 1;
|
||||
}
|
||||
}
|
||||
save_reduction(file, path, "remove_dump_var");
|
||||
}
|
||||
|
||||
/// Replaces matches with gotos where possible.
|
||||
/// This exploits some properties of rustlantis(match arm order),
|
||||
/// and is only soundly applicable to MIR generated by it.
|
||||
/// Still, it is not something `cvise` can do, but it simplifies the code a ton.
|
||||
fn match_to_goto(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
|
||||
let mut curr = 0;
|
||||
|
||||
while curr < file.len() {
|
||||
let Some(match_starts) = file[curr..].iter().position(|line| line.contains("match")) else {
|
||||
// No more `match`es to remove - exit early.
|
||||
break;
|
||||
};
|
||||
let match_starts = match_starts + curr;
|
||||
// Find the end of the match
|
||||
let Some(match_ends) = file[match_starts..].iter().position(|line| line.contains('}'))
|
||||
else {
|
||||
// Can't find match end - exit early.
|
||||
break;
|
||||
};
|
||||
let match_ends = match_ends + match_starts;
|
||||
let match_body = &file[match_starts..match_ends];
|
||||
|
||||
// Find where this match should normally jump to.
|
||||
// This *should* be the second-last arm of the match, as per the paper(the remaining blocks are decoys).
|
||||
// If this ever changes, this reduction may not always be sound.
|
||||
// This is not a problem, however: we NEED to use MIRI for reduction anwyway,
|
||||
// and it will catch this issue.
|
||||
let jumps_to = &match_body[match_body.len() - 2].trim();
|
||||
let Some((_, bb_ident)) = jumps_to.split_once("bb") else {
|
||||
break;
|
||||
};
|
||||
// We now have the number of the block we jump to at runtime.
|
||||
let bb_ident = bb_ident.trim_matches(',');
|
||||
// Try replacing this match with an unconditional jump.
|
||||
let mut file_copy = file.clone();
|
||||
for _ in match_starts..(match_ends + 1) {
|
||||
file_copy.remove(match_starts);
|
||||
}
|
||||
file_copy.insert(match_starts, format!("Goto(bb{bb_ident})\n"));
|
||||
if test_reduction(&file_copy, path, cache) {
|
||||
println!("Reduced {path:?} by {} lines `match_to_goto`", match_ends - match_starts);
|
||||
*file = file_copy;
|
||||
curr = match_starts;
|
||||
} else {
|
||||
curr = match_ends;
|
||||
}
|
||||
}
|
||||
save_reduction(file, path, "match_to_goto");
|
||||
}
|
||||
|
||||
/// At this point, we can try "killing" blocks, by replacing their bodies with calls to `abort`.
|
||||
/// This is always sound(the program aborts, so no UB can occur after the block),
|
||||
/// and allows us to safely remove *a lot* of unneeded blocks.
|
||||
fn block_abort(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
|
||||
let mut curr = 0;
|
||||
while curr < file.len() {
|
||||
let Some(block_starts) = file[curr..]
|
||||
.iter()
|
||||
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
|
||||
else {
|
||||
// No more `block`s to kill - exit early.
|
||||
break;
|
||||
};
|
||||
let block_starts = block_starts + curr;
|
||||
// Find the beginning of the next block to find the end of this block.
|
||||
let Some(block_ends) = file[(block_starts + 1)..]
|
||||
.iter()
|
||||
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
|
||||
else {
|
||||
// No more `block`s to kill - exit early.
|
||||
break;
|
||||
};
|
||||
let block_ends = block_starts + block_ends;
|
||||
let block_starts = block_starts + 1;
|
||||
let mut file_copy = file.clone();
|
||||
// Remove the block body...
|
||||
for _ in block_starts..(block_ends) {
|
||||
file_copy.remove(block_starts);
|
||||
}
|
||||
// ..and insert an unconditional call to abort.
|
||||
file_copy.insert(
|
||||
block_starts,
|
||||
"Call(tmp = core::intrinsics::abort(), ReturnTo(bb1), UnwindUnreachable())\n"
|
||||
.to_string(),
|
||||
);
|
||||
file_copy.insert(block_starts, "let tmp = ();\n".to_string());
|
||||
|
||||
if test_reduction(&file_copy, path, cache) {
|
||||
println!("Reduced {path:?} by {} lines `block_abort`", block_ends - block_starts - 2);
|
||||
*file = file_copy;
|
||||
curr = block_starts;
|
||||
} else {
|
||||
curr = block_ends;
|
||||
}
|
||||
}
|
||||
save_reduction(file, path, "block_abort");
|
||||
}
|
||||
|
||||
/// Removes unreachable basic blocks
|
||||
fn remove_block(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
|
||||
let mut curr = 0;
|
||||
|
||||
// Next, we try to outright remove blocks.
|
||||
while curr < file.len() {
|
||||
let Some(block_starts) = file[curr..]
|
||||
.iter()
|
||||
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
|
||||
else {
|
||||
// No more `block`s to remove - exit early.
|
||||
break;
|
||||
};
|
||||
let block_starts = block_starts + curr;
|
||||
// Find the beginning of the next block to find the end of this block.
|
||||
let Some(block_ends) = file[(block_starts + 1)..]
|
||||
.iter()
|
||||
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
|
||||
else {
|
||||
// No more `block`s to remove - exit early.
|
||||
break;
|
||||
};
|
||||
let block_ends = block_starts + block_ends + 1;
|
||||
// Large blocks are likely to be necessary.
|
||||
if block_ends - block_starts > 6 {
|
||||
curr = block_starts + 1;
|
||||
continue;
|
||||
}
|
||||
let mut file_copy = file.clone();
|
||||
file_copy.drain(block_starts..block_ends);
|
||||
if test_reduction(&file_copy, path, cache) {
|
||||
println!("Reduced {path:?} by {} lines `remove_blocks`", block_ends - block_starts);
|
||||
*file = file_copy;
|
||||
curr = block_starts;
|
||||
} else {
|
||||
curr = block_starts + 1;
|
||||
}
|
||||
}
|
||||
save_reduction(file, path, "remove_block");
|
||||
}
|
||||
|
||||
/// Merges blocks ending with unconditional jumps.
|
||||
fn linearize_cf(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
|
||||
let mut curr = 0;
|
||||
|
||||
// Next, we try to linearize the control flow. What the does that mean?
|
||||
// Given a sequence like this:
|
||||
// Goto(bb22)
|
||||
// }
|
||||
// bb22 = {
|
||||
// We remove those 3 lines, merging the blocks together. This is not something `cvise` can do,
|
||||
// and it makes other transformations easier.
|
||||
while curr < file.len() {
|
||||
let Some(block_starts) = file[curr..]
|
||||
.iter()
|
||||
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
|
||||
else {
|
||||
// No more `block`s to remove - exit early.
|
||||
break;
|
||||
};
|
||||
let block_starts = block_starts + curr;
|
||||
// Extract the block id.
|
||||
let Some((block, _)) = file[block_starts].split_once('=') else {
|
||||
curr = block_starts + 1;
|
||||
continue;
|
||||
};
|
||||
let block = block.trim();
|
||||
if file[block_starts - 2].trim() != format!("Goto({block})") {
|
||||
curr = block_starts + 1;
|
||||
continue;
|
||||
}
|
||||
let mut file_copy = file.clone();
|
||||
// Try removing 3 consecutive lines(the goto, block end and block beginning). This effectively removes a `Goto(next)`.
|
||||
file_copy.remove(block_starts - 2);
|
||||
file_copy.remove(block_starts - 2);
|
||||
file_copy.remove(block_starts - 2);
|
||||
// Check if this reduction is valid.
|
||||
if test_reduction(&file_copy, path, cache) {
|
||||
println!("Reduced {path:?} by 3 lines `linearize_cf`");
|
||||
*file = file_copy;
|
||||
curr = block_starts;
|
||||
} else {
|
||||
curr = block_starts + 1;
|
||||
}
|
||||
}
|
||||
save_reduction(file, path, "linearize_cf");
|
||||
}
|
||||
|
||||
/// Replaces a call to a given function with a 0 assignment to the destination place, and a Goto.
|
||||
/// This is always sound, because:
|
||||
/// 1. All the functions arguments are always initialized
|
||||
/// 2. and point to initialized memory(the operand of &raw must be an initialized place in rustlantis).
|
||||
fn remove_fn_calls(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
|
||||
let mut curr = 0;
|
||||
|
||||
while curr < file.len() {
|
||||
let Some(fn_call) =
|
||||
file[curr..].iter().position(|line| line.contains("Call(") && line.contains(" = fn"))
|
||||
else {
|
||||
// No more calls to remove - exit early.
|
||||
break;
|
||||
};
|
||||
let fn_call = fn_call + curr;
|
||||
let line = file[fn_call].trim();
|
||||
// Skip the Call(
|
||||
let line = &line["Call(".len()..];
|
||||
// Extract the destination place
|
||||
let Some((place, line)) = line.split_once('=') else {
|
||||
curr = fn_call + 1;
|
||||
continue;
|
||||
};
|
||||
// Skip till the return block id.
|
||||
let Some((_, line)) = line.split_once("ReturnTo(") else {
|
||||
curr = fn_call + 1;
|
||||
continue;
|
||||
};
|
||||
// Extract the full return block
|
||||
let Some((block, _)) = line.split_once(')') else {
|
||||
curr = fn_call + 1;
|
||||
continue;
|
||||
};
|
||||
let mut file_copy = file.clone();
|
||||
// Remove the call.
|
||||
file_copy.remove(fn_call);
|
||||
file_copy.insert(fn_call, format!("Goto({block})\n"));
|
||||
file_copy.insert(fn_call, format!("{place} = 0;\n"));
|
||||
// Check if this reduction is valid.
|
||||
if test_reduction(&file_copy, path, cache) {
|
||||
println!("Reduced {path:?} using `remove_fn_calls` {cache:?}");
|
||||
*file = file_copy;
|
||||
curr = fn_call;
|
||||
} else {
|
||||
curr = fn_call + 1;
|
||||
}
|
||||
}
|
||||
save_reduction(file, path, "remove_fn_calls");
|
||||
}
|
||||
|
||||
/// Fully removes unreachable functions.
|
||||
fn remove_fns(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
|
||||
let mut curr = 0;
|
||||
|
||||
while curr < file.len() {
|
||||
// Find a function start
|
||||
let Some(fn_start) = file[curr..].iter().position(|line| {
|
||||
line.contains("#[custom_mir(dialect = \"runtime\", phase = \"initial\")]")
|
||||
}) else {
|
||||
// No more functions to remove - exit early.
|
||||
break;
|
||||
};
|
||||
// Find the next function(and use that to find the end of this one).
|
||||
// FIXME: this check is flawed: it will never remove the very last function(the one before main).
|
||||
// The other checks will turn that function into a single call to abort, but it is still annoying that it is kept.
|
||||
let fn_start = fn_start + curr;
|
||||
let Some(fn_end) = file[(fn_start + 3)..].iter().position(|line| line.contains("fn fn"))
|
||||
else {
|
||||
// No more functions to remove - exit early.
|
||||
break;
|
||||
};
|
||||
let fn_end = fn_start + 2 + fn_end;
|
||||
let mut file_copy = file.clone();
|
||||
// Remove the function.\\
|
||||
file_copy.drain(fn_start..fn_end);
|
||||
// Check if this reduction is valid.
|
||||
if test_reduction(&file_copy, path, cache) {
|
||||
println!("Reduced {path:?} by {} lines `remove_fns`", fn_end - fn_start);
|
||||
*file = file_copy;
|
||||
} else {
|
||||
curr = fn_start + 1;
|
||||
}
|
||||
}
|
||||
save_reduction(file, path, "remove_fns");
|
||||
}
|
||||
|
||||
pub(super) fn reduce(path: impl AsRef<Path>) {
|
||||
let path = path.as_ref().to_owned();
|
||||
// ... read the file to a buffer ..
|
||||
let file = std::fs::read_to_string(&path).expect("Could not open the file to reduce");
|
||||
let mut file: Vec<_> = file.split_inclusive('\n').map(|s| s.to_string()).collect();
|
||||
|
||||
// ... and run reduction passes.
|
||||
println!("running `remove_dump_var` on {path:?}.");
|
||||
remove_dump_var(&mut file, &path);
|
||||
// After `dump_var`, the execution results ought not to change. Cache them.
|
||||
let mut cache = None;
|
||||
// Fill the cache
|
||||
assert!(
|
||||
test_reduction(&file, &path, &mut cache),
|
||||
"Reduction error: check that the input file is a valid reproducer."
|
||||
);
|
||||
println!("cache:{cache:?}");
|
||||
println!("running `remove_fn_calls` on {path:?}.");
|
||||
remove_fn_calls(&mut file, &path, &mut cache);
|
||||
println!("running `remove_fns` on {path:?}.");
|
||||
remove_fns(&mut file, &path, &mut cache);
|
||||
let len = file.len();
|
||||
println!("running `remove_dup_assign` on {path:?}.");
|
||||
remove_dup_assign(&mut file, &path, 0, len, &mut cache);
|
||||
file.retain(|line| !line.is_empty());
|
||||
println!("running `match_to_goto` on {path:?}.");
|
||||
match_to_goto(&mut file, &path, &mut cache);
|
||||
println!("running `block_abort` on {path:?}.");
|
||||
block_abort(&mut file, &path, &mut cache);
|
||||
println!("running `remove_block` on {path:?}.");
|
||||
remove_block(&mut file, &path, &mut cache);
|
||||
println!("running `linearize_cf` on {path:?}.");
|
||||
linearize_cf(&mut file, &path, &mut cache);
|
||||
let mut out = std::fs::File::create(&path).expect("Could not save the reduction result.");
|
||||
let file = file.into_iter().collect::<String>();
|
||||
out.write_all(file.as_bytes()).expect("failed to write into file");
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ pub fn run() -> Result<(), String> {
|
|||
config.no_download = true;
|
||||
config.setup_gcc_path()?;
|
||||
if let Some(gcc_path) = config.gcc_path {
|
||||
println!("{}", gcc_path);
|
||||
println!("{gcc_path}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ mod clean;
|
|||
mod clone_gcc;
|
||||
mod config;
|
||||
mod fmt;
|
||||
mod fuzz;
|
||||
mod info;
|
||||
mod prepare;
|
||||
mod rust_tools;
|
||||
|
|
@ -42,7 +43,8 @@ Commands:
|
|||
test : Runs tests for the project.
|
||||
info : Displays information about the build environment and project configuration.
|
||||
clone-gcc : Clones the GCC compiler from a specified source.
|
||||
fmt : Runs rustfmt"
|
||||
fmt : Runs rustfmt
|
||||
fuzz : Fuzzes `cg_gcc` using rustlantis"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +58,7 @@ pub enum Command {
|
|||
Test,
|
||||
Info,
|
||||
Fmt,
|
||||
Fuzz,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
@ -75,6 +78,7 @@ fn main() {
|
|||
Some("info") => Command::Info,
|
||||
Some("clone-gcc") => Command::CloneGcc,
|
||||
Some("fmt") => Command::Fmt,
|
||||
Some("fuzz") => Command::Fuzz,
|
||||
Some("--help") => {
|
||||
usage();
|
||||
process::exit(0);
|
||||
|
|
@ -97,6 +101,7 @@ fn main() {
|
|||
Command::Info => info::run(),
|
||||
Command::CloneGcc => clone_gcc::run(),
|
||||
Command::Fmt => fmt::run(),
|
||||
Command::Fuzz => fuzz::run(),
|
||||
} {
|
||||
eprintln!("Command failed to run: {e}");
|
||||
process::exit(1);
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ fn prepare_libcore(
|
|||
if let Some(path) = sysroot_source {
|
||||
rustlib_dir = Path::new(&path)
|
||||
.canonicalize()
|
||||
.map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
|
||||
.map_err(|error| format!("Failed to canonicalize path: {error:?}"))?;
|
||||
if !rustlib_dir.is_dir() {
|
||||
return Err(format!("Custom sysroot path {:?} not found", rustlib_dir));
|
||||
return Err(format!("Custom sysroot path {rustlib_dir:?} not found"));
|
||||
}
|
||||
} else {
|
||||
let rustc_path = match get_rustc_path() {
|
||||
|
|
@ -36,17 +36,17 @@ fn prepare_libcore(
|
|||
rustlib_dir = parent
|
||||
.join("../lib/rustlib/src/rust")
|
||||
.canonicalize()
|
||||
.map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
|
||||
.map_err(|error| format!("Failed to canonicalize path: {error:?}"))?;
|
||||
if !rustlib_dir.is_dir() {
|
||||
return Err("Please install `rust-src` component".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let sysroot_dir = sysroot_path.join("sysroot_src");
|
||||
if sysroot_dir.is_dir() {
|
||||
if let Err(error) = fs::remove_dir_all(&sysroot_dir) {
|
||||
return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), error,));
|
||||
}
|
||||
if sysroot_dir.is_dir()
|
||||
&& let Err(error) = fs::remove_dir_all(&sysroot_dir)
|
||||
{
|
||||
return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), error,));
|
||||
}
|
||||
|
||||
let sysroot_library_dir = sysroot_dir.join("library");
|
||||
|
|
@ -122,7 +122,7 @@ fn prepare_rand() -> Result<(), String> {
|
|||
// Apply patch for the rand crate.
|
||||
let file_path = "patches/crates/0001-Remove-deny-warnings.patch";
|
||||
let rand_dir = Path::new("build/rand");
|
||||
println!("[GIT] apply `{}`", file_path);
|
||||
println!("[GIT] apply `{file_path}`");
|
||||
let path = Path::new("../..").join(file_path);
|
||||
run_command_with_output(&[&"git", &"apply", &path], Some(rand_dir))?;
|
||||
run_command_with_output(&[&"git", &"add", &"-A"], Some(rand_dir))?;
|
||||
|
|
@ -149,7 +149,7 @@ fn clone_and_setup<F>(repo_url: &str, checkout_commit: &str, extra: Option<F>) -
|
|||
where
|
||||
F: Fn(&Path) -> Result<(), String>,
|
||||
{
|
||||
let clone_result = git_clone_root_dir(repo_url, &Path::new(crate::BUILD_DIR), false)?;
|
||||
let clone_result = git_clone_root_dir(repo_url, Path::new(crate::BUILD_DIR), false)?;
|
||||
if !clone_result.ran_clone {
|
||||
println!("`{}` has already been cloned", clone_result.repo_name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,22 @@
|
|||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::config::ConfigInfo;
|
||||
use crate::utils::{
|
||||
get_toolchain, run_command_with_output_and_env_no_err, rustc_toolchain_version_info,
|
||||
rustc_version_info,
|
||||
};
|
||||
use crate::utils::{get_toolchain, rustc_toolchain_version_info, rustc_version_info};
|
||||
|
||||
fn args(command: &str) -> Result<Option<Vec<String>>, String> {
|
||||
// We skip the binary and the "cargo"/"rustc" option.
|
||||
if let Some("--help") = std::env::args().skip(2).next().as_deref() {
|
||||
if let Some("--help") = std::env::args().nth(2).as_deref() {
|
||||
usage(command);
|
||||
return Ok(None);
|
||||
}
|
||||
let args = std::env::args().skip(2).collect::<Vec<_>>();
|
||||
if args.is_empty() {
|
||||
return Err(format!(
|
||||
"Expected at least one argument for `{}` subcommand, found none",
|
||||
command
|
||||
"Expected at least one argument for `{command}` subcommand, found none"
|
||||
));
|
||||
}
|
||||
Ok(Some(args))
|
||||
|
|
@ -27,12 +25,11 @@ fn args(command: &str) -> Result<Option<Vec<String>>, String> {
|
|||
fn usage(command: &str) {
|
||||
println!(
|
||||
r#"
|
||||
`{}` command help:
|
||||
`{command}` command help:
|
||||
|
||||
[args] : Arguments to be passed to the cargo command
|
||||
--help : Show this help
|
||||
"#,
|
||||
command,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -51,10 +48,10 @@ impl RustcTools {
|
|||
// expected.
|
||||
let current_dir = std::env::current_dir()
|
||||
.and_then(|path| path.canonicalize())
|
||||
.map_err(|error| format!("Failed to get current directory path: {:?}", error))?;
|
||||
.map_err(|error| format!("Failed to get current directory path: {error:?}"))?;
|
||||
let current_exe = std::env::current_exe()
|
||||
.and_then(|path| path.canonicalize())
|
||||
.map_err(|error| format!("Failed to get current exe path: {:?}", error))?;
|
||||
.map_err(|error| format!("Failed to get current exe path: {error:?}"))?;
|
||||
let mut parent_dir =
|
||||
current_exe.components().map(|comp| comp.as_os_str()).collect::<Vec<_>>();
|
||||
// We run this script from "build_system/target/release/y", so we need to remove these elements.
|
||||
|
|
@ -68,7 +65,7 @@ impl RustcTools {
|
|||
));
|
||||
}
|
||||
}
|
||||
let parent_dir = PathBuf::from(parent_dir.join(&OsStr::new("/")));
|
||||
let parent_dir = PathBuf::from(parent_dir.join(OsStr::new("/")));
|
||||
std::env::set_current_dir(&parent_dir).map_err(|error| {
|
||||
format!("Failed to go to `{}` folder: {:?}", parent_dir.display(), error)
|
||||
})?;
|
||||
|
|
@ -92,11 +89,31 @@ impl RustcTools {
|
|||
std::env::set_current_dir(¤t_dir).map_err(|error| {
|
||||
format!("Failed to go back to `{}` folder: {:?}", current_dir.display(), error)
|
||||
})?;
|
||||
let toolchain = format!("+{}", toolchain);
|
||||
let toolchain = format!("+{toolchain}");
|
||||
Ok(Some(Self { toolchain, args, env, config }))
|
||||
}
|
||||
}
|
||||
|
||||
fn exec(input: &[&dyn AsRef<OsStr>], env: &HashMap<String, String>) -> Result<(), String> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
// We use `exec` to call the `execvp` syscall instead of creating a new process where the
|
||||
// command will be executed because very few signals can actually kill a current process,
|
||||
// so if segmentation fault (SIGSEGV signal) happens and we raise to the current process,
|
||||
// it will simply do nothing and we won't have the nice error message for the shell.
|
||||
let error = crate::utils::get_command_inner(input, None, Some(env)).exec();
|
||||
eprintln!("execvp syscall failed: {error:?}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
if crate::utils::run_command_with_output_and_env_no_err(input, None, Some(env)).is_err() {
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_cargo() -> Result<(), String> {
|
||||
let Some(mut tools) = RustcTools::new("cargo")? else { return Ok(()) };
|
||||
let rustflags = tools.env.get("RUSTFLAGS").cloned().unwrap_or_default();
|
||||
|
|
@ -105,11 +122,7 @@ pub fn run_cargo() -> Result<(), String> {
|
|||
for arg in &tools.args {
|
||||
command.push(arg);
|
||||
}
|
||||
if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
exec(&command, &tools.env)
|
||||
}
|
||||
|
||||
pub fn run_rustc() -> Result<(), String> {
|
||||
|
|
@ -118,8 +131,5 @@ pub fn run_rustc() -> Result<(), String> {
|
|||
for arg in &tools.args {
|
||||
command.push(arg);
|
||||
}
|
||||
if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(())
|
||||
exec(&command, &tools.env)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use crate::build;
|
|||
use crate::config::{Channel, ConfigInfo};
|
||||
use crate::utils::{
|
||||
create_dir, get_sysroot_dir, get_toolchain, git_clone, git_clone_root_dir, remove_file,
|
||||
run_command, run_command_with_env, run_command_with_output_and_env, rustc_version_info,
|
||||
split_args, walk_dir,
|
||||
run_command, run_command_with_env, run_command_with_output, run_command_with_output_and_env,
|
||||
rustc_version_info, split_args, walk_dir,
|
||||
};
|
||||
|
||||
type Env = HashMap<String, String>;
|
||||
|
|
@ -42,7 +42,7 @@ fn get_runners() -> Runners {
|
|||
);
|
||||
runners.insert("--extended-regex-tests", ("Run extended regex tests", extended_regex_tests));
|
||||
runners.insert("--mini-tests", ("Run mini tests", mini_tests));
|
||||
|
||||
runners.insert("--cargo-tests", ("Run cargo tests", cargo_tests));
|
||||
runners
|
||||
}
|
||||
|
||||
|
|
@ -53,9 +53,9 @@ fn get_number_after_arg(
|
|||
match args.next() {
|
||||
Some(nb) if !nb.is_empty() => match usize::from_str(&nb) {
|
||||
Ok(nb) => Ok(nb),
|
||||
Err(_) => Err(format!("Expected a number after `{}`, found `{}`", option, nb)),
|
||||
Err(_) => Err(format!("Expected a number after `{option}`, found `{nb}`")),
|
||||
},
|
||||
_ => Err(format!("Expected a number after `{}`, found nothing", option)),
|
||||
_ => Err(format!("Expected a number after `{option}`, found nothing")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,8 +76,8 @@ fn show_usage() {
|
|||
for (option, (doc, _)) in get_runners() {
|
||||
// FIXME: Instead of using the hard-coded `23` value, better to compute it instead.
|
||||
let needed_spaces = 23_usize.saturating_sub(option.len());
|
||||
let spaces: String = std::iter::repeat(' ').take(needed_spaces).collect();
|
||||
println!(" {}{}: {}", option, spaces, doc);
|
||||
let spaces: String = std::iter::repeat_n(' ', needed_spaces).collect();
|
||||
println!(" {option}{spaces}: {doc}");
|
||||
}
|
||||
println!(" --help : Show this help");
|
||||
}
|
||||
|
|
@ -88,6 +88,8 @@ struct TestArg {
|
|||
use_system_gcc: bool,
|
||||
runners: Vec<String>,
|
||||
flags: Vec<String>,
|
||||
/// Additional arguments, to be passed to commands like `cargo test`.
|
||||
test_args: Vec<String>,
|
||||
nb_parts: Option<usize>,
|
||||
current_part: Option<usize>,
|
||||
sysroot_panic_abort: bool,
|
||||
|
|
@ -137,13 +139,14 @@ impl TestArg {
|
|||
test_arg.sysroot_features.push(feature);
|
||||
}
|
||||
_ => {
|
||||
return Err(format!("Expected an argument after `{}`, found nothing", arg));
|
||||
return Err(format!("Expected an argument after `{arg}`, found nothing"));
|
||||
}
|
||||
},
|
||||
"--help" => {
|
||||
show_usage();
|
||||
return Ok(None);
|
||||
}
|
||||
"--" => test_arg.test_args.extend(&mut args),
|
||||
x if runners.contains_key(x)
|
||||
&& !test_arg.runners.iter().any(|runner| runner == x) =>
|
||||
{
|
||||
|
|
@ -151,7 +154,7 @@ impl TestArg {
|
|||
}
|
||||
arg => {
|
||||
if !test_arg.config_info.parse_argument(arg, &mut args)? {
|
||||
return Err(format!("Unknown option {}", arg));
|
||||
return Err(format!("Unknown option {arg}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -189,7 +192,7 @@ fn build_if_no_backend(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
command.push(&"--release");
|
||||
&tmp_env
|
||||
} else {
|
||||
&env
|
||||
env
|
||||
};
|
||||
for flag in args.flags.iter() {
|
||||
command.push(flag);
|
||||
|
|
@ -203,6 +206,33 @@ fn clean(_env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
create_dir(&path)
|
||||
}
|
||||
|
||||
fn cargo_tests(test_env: &Env, test_args: &TestArg) -> Result<(), String> {
|
||||
// First, we call `mini_tests` to build minicore for us. This ensures we are testing with a working `minicore`,
|
||||
// and that any changes we have made affect `minicore`(since it would get rebuilt).
|
||||
mini_tests(test_env, test_args)?;
|
||||
// Then, we copy some of the env vars from `test_env`
|
||||
// We don't want to pass things like `RUSTFLAGS`, since they contain the -Zcodegen-backend flag.
|
||||
// That would force `cg_gcc` to *rebuild itself* and only then run tests, which is undesirable.
|
||||
let mut env = HashMap::new();
|
||||
env.insert(
|
||||
"LD_LIBRARY_PATH".into(),
|
||||
test_env.get("LD_LIBRARY_PATH").expect("LD_LIBRARY_PATH missing!").to_string(),
|
||||
);
|
||||
env.insert(
|
||||
"LIBRARY_PATH".into(),
|
||||
test_env.get("LIBRARY_PATH").expect("LIBRARY_PATH missing!").to_string(),
|
||||
);
|
||||
env.insert(
|
||||
"CG_RUSTFLAGS".into(),
|
||||
test_env.get("CG_RUSTFLAGS").map(|s| s.as_str()).unwrap_or("").to_string(),
|
||||
);
|
||||
// Pass all the default args + the user-specified ones.
|
||||
let mut args: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"test"];
|
||||
args.extend(test_args.test_args.iter().map(|s| s as &dyn AsRef<OsStr>));
|
||||
run_command_with_output_and_env(&args, None, Some(&env))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
|
||||
// FIXME: create a function "display_if_not_quiet" or something along the line.
|
||||
println!("[BUILD] mini_core");
|
||||
|
|
@ -222,7 +252,7 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
&"--target",
|
||||
&args.config_info.target_triple,
|
||||
]);
|
||||
run_command_with_output_and_env(&command, None, Some(&env))?;
|
||||
run_command_with_output_and_env(&command, None, Some(env))?;
|
||||
|
||||
// FIXME: create a function "display_if_not_quiet" or something along the line.
|
||||
println!("[BUILD] example");
|
||||
|
|
@ -234,7 +264,7 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
&"--target",
|
||||
&args.config_info.target_triple,
|
||||
]);
|
||||
run_command_with_output_and_env(&command, None, Some(&env))?;
|
||||
run_command_with_output_and_env(&command, None, Some(env))?;
|
||||
|
||||
// FIXME: create a function "display_if_not_quiet" or something along the line.
|
||||
println!("[AOT] mini_core_hello_world");
|
||||
|
|
@ -249,14 +279,14 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
&"--target",
|
||||
&args.config_info.target_triple,
|
||||
]);
|
||||
run_command_with_output_and_env(&command, None, Some(&env))?;
|
||||
run_command_with_output_and_env(&command, None, Some(env))?;
|
||||
|
||||
let command: &[&dyn AsRef<OsStr>] = &[
|
||||
&Path::new(&args.config_info.cargo_target_dir).join("mini_core_hello_world"),
|
||||
&"abc",
|
||||
&"bcd",
|
||||
];
|
||||
maybe_run_command_in_vm(&command, env, args)?;
|
||||
maybe_run_command_in_vm(command, env, args)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -454,22 +484,47 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
|
|||
} else {
|
||||
run_command_with_output_and_env(&[&"git", &"checkout"], rust_dir, Some(env))?;
|
||||
}
|
||||
|
||||
let mut patches = Vec::new();
|
||||
walk_dir(
|
||||
"patches/tests",
|
||||
&mut |_| Ok(()),
|
||||
&mut |file_path: &Path| {
|
||||
patches.push(file_path.to_path_buf());
|
||||
Ok(())
|
||||
},
|
||||
false,
|
||||
)?;
|
||||
patches.sort();
|
||||
// TODO: remove duplication with prepare.rs by creating a apply_patch function in the utils
|
||||
// module.
|
||||
for file_path in patches {
|
||||
println!("[GIT] apply `{}`", file_path.display());
|
||||
let path = Path::new("../..").join(file_path);
|
||||
run_command_with_output(&[&"git", &"apply", &path], rust_dir)?;
|
||||
run_command_with_output(&[&"git", &"add", &"-A"], rust_dir)?;
|
||||
run_command_with_output(
|
||||
&[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())],
|
||||
rust_dir,
|
||||
)?;
|
||||
}
|
||||
|
||||
let cargo = String::from_utf8(
|
||||
run_command_with_env(&[&"rustup", &"which", &"cargo"], rust_dir, Some(env))?.stdout,
|
||||
)
|
||||
.map_err(|error| format!("Failed to retrieve cargo path: {:?}", error))
|
||||
.map_err(|error| format!("Failed to retrieve cargo path: {error:?}"))
|
||||
.and_then(|cargo| {
|
||||
let cargo = cargo.trim().to_owned();
|
||||
if cargo.is_empty() { Err(format!("`cargo` path is empty")) } else { Ok(cargo) }
|
||||
if cargo.is_empty() { Err("`cargo` path is empty".to_string()) } else { Ok(cargo) }
|
||||
})?;
|
||||
let rustc = String::from_utf8(
|
||||
run_command_with_env(&[&"rustup", &toolchain, &"which", &"rustc"], rust_dir, Some(env))?
|
||||
.stdout,
|
||||
)
|
||||
.map_err(|error| format!("Failed to retrieve rustc path: {:?}", error))
|
||||
.map_err(|error| format!("Failed to retrieve rustc path: {error:?}"))
|
||||
.and_then(|rustc| {
|
||||
let rustc = rustc.trim().to_owned();
|
||||
if rustc.is_empty() { Err(format!("`rustc` path is empty")) } else { Ok(rustc) }
|
||||
if rustc.is_empty() { Err("`rustc` path is empty".to_string()) } else { Ok(rustc) }
|
||||
})?;
|
||||
let llvm_filecheck = match run_command_with_env(
|
||||
&[
|
||||
|
|
@ -479,7 +534,8 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
|
|||
which FileCheck-11 || \
|
||||
which FileCheck-12 || \
|
||||
which FileCheck-13 || \
|
||||
which FileCheck-14",
|
||||
which FileCheck-14 || \
|
||||
which FileCheck",
|
||||
],
|
||||
rust_dir,
|
||||
Some(env),
|
||||
|
|
@ -487,13 +543,15 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
|
|||
Ok(cmd) => String::from_utf8_lossy(&cmd.stdout).to_string(),
|
||||
Err(_) => {
|
||||
eprintln!("Failed to retrieve LLVM FileCheck, ignoring...");
|
||||
// FIXME: the test tests/run-make/no-builtins-attribute will fail if we cannot find
|
||||
// FileCheck.
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
let file_path = rust_dir_path.join("config.toml");
|
||||
std::fs::write(
|
||||
&file_path,
|
||||
&format!(
|
||||
format!(
|
||||
r#"change-id = 115898
|
||||
|
||||
[rust]
|
||||
|
|
@ -532,7 +590,7 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
let codegen_backend_path = format!(
|
||||
"{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}",
|
||||
pwd = std::env::current_dir()
|
||||
.map_err(|error| format!("`current_dir` failed: {:?}", error))?
|
||||
.map_err(|error| format!("`current_dir` failed: {error:?}"))?
|
||||
.display(),
|
||||
channel = args.config_info.channel.as_str(),
|
||||
dylib_ext = args.config_info.dylib_ext,
|
||||
|
|
@ -587,11 +645,11 @@ where
|
|||
F: Fn(&[&dyn AsRef<OsStr>], Option<&Path>, &Env) -> Result<(), String>,
|
||||
{
|
||||
let toolchain = get_toolchain()?;
|
||||
let toolchain_arg = format!("+{}", toolchain);
|
||||
let toolchain_arg = format!("+{toolchain}");
|
||||
let rustc_version = String::from_utf8(
|
||||
run_command_with_env(&[&args.config_info.rustc_command[0], &"-V"], cwd, Some(env))?.stdout,
|
||||
)
|
||||
.map_err(|error| format!("Failed to retrieve rustc version: {:?}", error))?;
|
||||
.map_err(|error| format!("Failed to retrieve rustc version: {error:?}"))?;
|
||||
let rustc_toolchain_version = String::from_utf8(
|
||||
run_command_with_env(
|
||||
&[&args.config_info.rustc_command[0], &toolchain_arg, &"-V"],
|
||||
|
|
@ -600,20 +658,19 @@ where
|
|||
)?
|
||||
.stdout,
|
||||
)
|
||||
.map_err(|error| format!("Failed to retrieve rustc +toolchain version: {:?}", error))?;
|
||||
.map_err(|error| format!("Failed to retrieve rustc +toolchain version: {error:?}"))?;
|
||||
|
||||
if rustc_version != rustc_toolchain_version {
|
||||
eprintln!(
|
||||
"rustc_codegen_gcc is built for `{}` but the default rustc version is `{}`.",
|
||||
rustc_toolchain_version, rustc_version,
|
||||
"rustc_codegen_gcc is built for `{rustc_toolchain_version}` but the default rustc version is `{rustc_version}`.",
|
||||
);
|
||||
eprintln!("Using `{}`.", rustc_toolchain_version);
|
||||
eprintln!("Using `{rustc_toolchain_version}`.");
|
||||
}
|
||||
let mut env = env.clone();
|
||||
let rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default();
|
||||
env.insert("RUSTDOCFLAGS".to_string(), rustflags);
|
||||
let mut cargo_command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &toolchain_arg];
|
||||
cargo_command.extend_from_slice(&command);
|
||||
cargo_command.extend_from_slice(command);
|
||||
callback(&cargo_command, cwd, &env)
|
||||
}
|
||||
|
||||
|
|
@ -680,7 +737,15 @@ fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
println!("[TEST] libcore");
|
||||
let path = get_sysroot_dir().join("sysroot_src/library/coretests");
|
||||
let _ = remove_dir_all(path.join("target"));
|
||||
run_cargo_command(&[&"test"], Some(&path), env, args)?;
|
||||
// TODO(antoyo): run in release mode when we fix the failures.
|
||||
// TODO(antoyo): remove the --skip f16::test_total_cmp when this issue is fixed:
|
||||
// https://github.com/rust-lang/rust/issues/141503
|
||||
run_cargo_command(
|
||||
&[&"test", &"--", &"--skip", &"f16::test_total_cmp"],
|
||||
Some(&path),
|
||||
env,
|
||||
args,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -818,7 +883,7 @@ fn contains_ui_error_patterns(file_path: &Path, keep_lto_tests: bool) -> Result<
|
|||
// Tests generating errors.
|
||||
let file = File::open(file_path)
|
||||
.map_err(|error| format!("Failed to read `{}`: {:?}", file_path.display(), error))?;
|
||||
for line in BufReader::new(file).lines().filter_map(|line| line.ok()) {
|
||||
for line in BufReader::new(file).lines().map_while(Result::ok) {
|
||||
let line = line.trim();
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
|
|
@ -887,7 +952,7 @@ where
|
|||
|
||||
if !prepare_files_callback(&rust_path)? {
|
||||
// FIXME: create a function "display_if_not_quiet" or something along the line.
|
||||
println!("Keeping all {} tests", test_type);
|
||||
println!("Keeping all {test_type} tests");
|
||||
}
|
||||
|
||||
if test_type == "ui" {
|
||||
|
|
@ -919,8 +984,7 @@ where
|
|||
"borrowck",
|
||||
"test-attrs",
|
||||
]
|
||||
.iter()
|
||||
.any(|name| *name == dir_name)
|
||||
.contains(&dir_name)
|
||||
{
|
||||
remove_dir_all(dir).map_err(|error| {
|
||||
format!("Failed to remove folder `{}`: {:?}", dir.display(), error)
|
||||
|
|
@ -975,10 +1039,7 @@ where
|
|||
if nb_parts > 0 {
|
||||
let current_part = args.current_part.unwrap();
|
||||
// FIXME: create a function "display_if_not_quiet" or something along the line.
|
||||
println!(
|
||||
"Splitting ui_test into {} parts (and running part {})",
|
||||
nb_parts, current_part
|
||||
);
|
||||
println!("Splitting ui_test into {nb_parts} parts (and running part {current_part})");
|
||||
let out = String::from_utf8(
|
||||
run_command(
|
||||
&[
|
||||
|
|
@ -996,7 +1057,7 @@ where
|
|||
)?
|
||||
.stdout,
|
||||
)
|
||||
.map_err(|error| format!("Failed to retrieve output of find command: {:?}", error))?;
|
||||
.map_err(|error| format!("Failed to retrieve output of find command: {error:?}"))?;
|
||||
let mut files = out
|
||||
.split('\n')
|
||||
.map(|line| line.trim())
|
||||
|
|
@ -1016,7 +1077,7 @@ where
|
|||
}
|
||||
|
||||
// FIXME: create a function "display_if_not_quiet" or something along the line.
|
||||
println!("[TEST] rustc {} test suite", test_type);
|
||||
println!("[TEST] rustc {test_type} test suite");
|
||||
env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
|
||||
|
||||
let extra =
|
||||
|
|
@ -1040,7 +1101,7 @@ where
|
|||
&"always",
|
||||
&"--stage",
|
||||
&"0",
|
||||
&format!("tests/{}", test_type),
|
||||
&format!("tests/{test_type}"),
|
||||
&"--compiletest-rustc-args",
|
||||
&rustc_args,
|
||||
],
|
||||
|
|
@ -1051,19 +1112,18 @@ where
|
|||
}
|
||||
|
||||
fn test_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
|
||||
//test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?;
|
||||
test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?;
|
||||
test_rustc_inner(env, args, |_| Ok(false), false, "ui")
|
||||
}
|
||||
|
||||
fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
|
||||
let result1 = Ok(());
|
||||
/*test_rustc_inner(
|
||||
let result1 = test_rustc_inner(
|
||||
env,
|
||||
args,
|
||||
retain_files_callback("tests/failing-run-make-tests.txt", "run-make"),
|
||||
false,
|
||||
"run-make",
|
||||
)*/
|
||||
);
|
||||
|
||||
let result2 = test_rustc_inner(
|
||||
env,
|
||||
|
|
@ -1084,14 +1144,13 @@ fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
false,
|
||||
"ui",
|
||||
)?;
|
||||
Ok(())
|
||||
/*test_rustc_inner(
|
||||
test_rustc_inner(
|
||||
env,
|
||||
args,
|
||||
remove_files_callback("tests/failing-run-make-tests.txt", "run-make"),
|
||||
false,
|
||||
"run-make",
|
||||
)*/
|
||||
)
|
||||
}
|
||||
|
||||
fn test_failing_ui_pattern_tests(env: &Env, args: &TestArg) -> Result<(), String> {
|
||||
|
|
@ -1118,7 +1177,7 @@ fn retain_files_callback<'a>(
|
|||
run_command(
|
||||
&[
|
||||
&"find",
|
||||
&format!("tests/{}", test_type),
|
||||
&format!("tests/{test_type}"),
|
||||
&"-mindepth",
|
||||
&"1",
|
||||
&"-type",
|
||||
|
|
@ -1137,7 +1196,7 @@ fn retain_files_callback<'a>(
|
|||
run_command(
|
||||
&[
|
||||
&"find",
|
||||
&format!("tests/{}", test_type),
|
||||
&format!("tests/{test_type}"),
|
||||
&"-type",
|
||||
&"f",
|
||||
&"-name",
|
||||
|
|
@ -1152,15 +1211,12 @@ fn retain_files_callback<'a>(
|
|||
}
|
||||
|
||||
// Putting back only the failing ones.
|
||||
if let Ok(files) = std::fs::read_to_string(&file_path) {
|
||||
if let Ok(files) = std::fs::read_to_string(file_path) {
|
||||
for file in files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty()) {
|
||||
run_command(&[&"git", &"checkout", &"--", &file], Some(&rust_path))?;
|
||||
run_command(&[&"git", &"checkout", &"--", &file], Some(rust_path))?;
|
||||
}
|
||||
} else {
|
||||
println!(
|
||||
"Failed to read `{}`, not putting back failing {} tests",
|
||||
file_path, test_type
|
||||
);
|
||||
println!("Failed to read `{file_path}`, not putting back failing {test_type} tests");
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
|
|
@ -1188,8 +1244,7 @@ fn remove_files_callback<'a>(
|
|||
}
|
||||
} else {
|
||||
println!(
|
||||
"Failed to read `{}`, not putting back failing {} tests",
|
||||
file_path, test_type
|
||||
"Failed to read `{file_path}`, not putting back failing {test_type} tests"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1202,7 +1257,7 @@ fn remove_files_callback<'a>(
|
|||
remove_file(&path)?;
|
||||
}
|
||||
} else {
|
||||
println!("Failed to read `{}`, not putting back failing ui tests", file_path);
|
||||
println!("Failed to read `{file_path}`, not putting back failing ui tests");
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
|
|
@ -1217,7 +1272,9 @@ fn run_all(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
// asm_tests(env, args)?;
|
||||
test_libcore(env, args)?;
|
||||
extended_sysroot_tests(env, args)?;
|
||||
cargo_tests(env, args)?;
|
||||
test_rustc(env, args)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
#[cfg(unix)]
|
||||
use std::ffi::c_int;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
#[cfg(unix)]
|
||||
|
|
@ -9,11 +7,6 @@ use std::os::unix::process::ExitStatusExt;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, ExitStatus, Output};
|
||||
|
||||
#[cfg(unix)]
|
||||
unsafe extern "C" {
|
||||
fn raise(signal: c_int) -> c_int;
|
||||
}
|
||||
|
||||
fn exec_command(
|
||||
input: &[&dyn AsRef<OsStr>],
|
||||
cwd: Option<&Path>,
|
||||
|
|
@ -27,17 +20,14 @@ fn exec_command(
|
|||
#[cfg(unix)]
|
||||
{
|
||||
if let Some(signal) = status.signal() {
|
||||
unsafe {
|
||||
raise(signal as _);
|
||||
}
|
||||
// In case the signal didn't kill the current process.
|
||||
return Err(command_error(input, &cwd, format!("Process received signal {}", signal)));
|
||||
return Err(command_error(input, &cwd, format!("Process received signal {signal}")));
|
||||
}
|
||||
}
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
fn get_command_inner(
|
||||
pub(crate) fn get_command_inner(
|
||||
input: &[&dyn AsRef<OsStr>],
|
||||
cwd: Option<&Path>,
|
||||
env: Option<&HashMap<String, String>>,
|
||||
|
|
@ -75,18 +65,18 @@ fn check_exit_status(
|
|||
);
|
||||
let input = input.iter().map(|i| i.as_ref()).collect::<Vec<&OsStr>>();
|
||||
if show_err {
|
||||
eprintln!("Command `{:?}` failed", input);
|
||||
eprintln!("Command `{input:?}` failed");
|
||||
}
|
||||
if let Some(output) = output {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
if !stdout.is_empty() {
|
||||
error.push_str("\n==== STDOUT ====\n");
|
||||
error.push_str(&*stdout);
|
||||
error.push_str(&stdout);
|
||||
}
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
if !stderr.is_empty() {
|
||||
error.push_str("\n==== STDERR ====\n");
|
||||
error.push_str(&*stderr);
|
||||
error.push_str(&stderr);
|
||||
}
|
||||
}
|
||||
Err(error)
|
||||
|
|
@ -136,6 +126,7 @@ pub fn run_command_with_output_and_env(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub fn run_command_with_output_and_env_no_err(
|
||||
input: &[&dyn AsRef<OsStr>],
|
||||
cwd: Option<&Path>,
|
||||
|
|
@ -242,7 +233,7 @@ pub fn get_toolchain() -> Result<String, String> {
|
|||
if !line.starts_with("channel") {
|
||||
return None;
|
||||
}
|
||||
line.split('"').skip(1).next()
|
||||
line.split('"').nth(1)
|
||||
})
|
||||
.next()
|
||||
{
|
||||
|
|
@ -281,7 +272,7 @@ fn git_clone_inner(
|
|||
}
|
||||
|
||||
fn get_repo_name(url: &str) -> String {
|
||||
let repo_name = url.split('/').last().unwrap();
|
||||
let repo_name = url.split('/').next_back().unwrap();
|
||||
match repo_name.strip_suffix(".git") {
|
||||
Some(n) => n.to_string(),
|
||||
None => repo_name.to_string(),
|
||||
|
|
|
|||
|
|
@ -77,18 +77,18 @@ fn main() {
|
|||
assert_eq!(tmp as i128, -0x1234_5678_9ABC_DEF0i128);
|
||||
|
||||
// Check that all u/i128 <-> float casts work correctly.
|
||||
let houndred_u128 = 100u128;
|
||||
let houndred_i128 = 100i128;
|
||||
let houndred_f32 = 100.0f32;
|
||||
let houndred_f64 = 100.0f64;
|
||||
assert_eq!(houndred_u128 as f32, 100.0);
|
||||
assert_eq!(houndred_u128 as f64, 100.0);
|
||||
assert_eq!(houndred_f32 as u128, 100);
|
||||
assert_eq!(houndred_f64 as u128, 100);
|
||||
assert_eq!(houndred_i128 as f32, 100.0);
|
||||
assert_eq!(houndred_i128 as f64, 100.0);
|
||||
assert_eq!(houndred_f32 as i128, 100);
|
||||
assert_eq!(houndred_f64 as i128, 100);
|
||||
let hundred_u128 = 100u128;
|
||||
let hundred_i128 = 100i128;
|
||||
let hundred_f32 = 100.0f32;
|
||||
let hundred_f64 = 100.0f64;
|
||||
assert_eq!(hundred_u128 as f32, 100.0);
|
||||
assert_eq!(hundred_u128 as f64, 100.0);
|
||||
assert_eq!(hundred_f32 as u128, 100);
|
||||
assert_eq!(hundred_f64 as u128, 100);
|
||||
assert_eq!(hundred_i128 as f32, 100.0);
|
||||
assert_eq!(hundred_i128 as f64, 100.0);
|
||||
assert_eq!(hundred_f32 as i128, 100);
|
||||
assert_eq!(hundred_f64 as i128, 100);
|
||||
|
||||
let _a = 1u32 << 2u8;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
codegen_gcc_unknown_ctarget_feature_prefix =
|
||||
unknown feature specified for `-Ctarget-feature`: `{$feature}`
|
||||
.note = features must begin with a `+` to enable or `-` to disable it
|
||||
|
||||
codegen_gcc_unwinding_inline_asm =
|
||||
GCC backend does not support unwinding from inline asm
|
||||
|
||||
|
|
@ -16,15 +12,3 @@ codegen_gcc_lto_disallowed = lto can only be run for executables, cdylibs and st
|
|||
codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
|
||||
|
||||
codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})
|
||||
|
||||
codegen_gcc_unknown_ctarget_feature =
|
||||
unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
|
||||
.note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
|
||||
.possible_feature = you might have meant: `{$rust_feature}`
|
||||
.consider_filing_feature_request = consider filing a feature request
|
||||
|
||||
codegen_gcc_missing_features =
|
||||
add the missing features in a `target_feature` attribute
|
||||
|
||||
codegen_gcc_target_feature_disable_or_enable =
|
||||
the target features {$features} must all be either enabled or disabled together
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
From cdb3d407740e4f15c3746051f8ba89b8e74e99d3 Mon Sep 17 00:00:00 2001
|
||||
From: None <none@example.com>
|
||||
Date: Fri, 30 May 2025 13:46:22 -0400
|
||||
Subject: [PATCH] Pin compiler_builtins to 0.1.160
|
||||
|
||||
---
|
||||
library/alloc/Cargo.toml | 2 +-
|
||||
library/std/Cargo.toml | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml
|
||||
index 9d0d957..365c9dc 100644
|
||||
--- a/library/alloc/Cargo.toml
|
||||
+++ b/library/alloc/Cargo.toml
|
||||
@@ -16,7 +16,7 @@ bench = false
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core", public = true }
|
||||
-compiler_builtins = { version = "=0.1.159", features = ['rustc-dep-of-std'] }
|
||||
+compiler_builtins = { version = "=0.1.160", features = ['rustc-dep-of-std'] }
|
||||
|
||||
[features]
|
||||
compiler-builtins-mem = ['compiler_builtins/mem']
|
||||
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
|
||||
index 4ff4895..31371f0 100644
|
||||
--- a/library/std/Cargo.toml
|
||||
+++ b/library/std/Cargo.toml
|
||||
@@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
|
||||
panic_unwind = { path = "../panic_unwind", optional = true }
|
||||
panic_abort = { path = "../panic_abort" }
|
||||
core = { path = "../core", public = true }
|
||||
-compiler_builtins = { version = "=0.1.159" }
|
||||
+compiler_builtins = { version = "=0.1.160" }
|
||||
unwind = { path = "../unwind" }
|
||||
hashbrown = { version = "0.15", default-features = false, features = [
|
||||
'rustc-dep-of-std',
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
From a131c69e54b5c02fe3b517e8f3ad23d4f784ffc8 Mon Sep 17 00:00:00 2001
|
||||
From: Antoni Boucher <bouanto@zoho.com>
|
||||
Date: Fri, 13 Jun 2025 20:25:33 -0400
|
||||
Subject: [PATCH] Workaround to make a run-make test pass
|
||||
|
||||
---
|
||||
tests/run-make/linker-warning/rmake.rs | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs
|
||||
index bc21739fefc..0946a7e2a48 100644
|
||||
--- a/tests/run-make/linker-warning/rmake.rs
|
||||
+++ b/tests/run-make/linker-warning/rmake.rs
|
||||
@@ -55,7 +55,7 @@ fn main() {
|
||||
diff()
|
||||
.expected_file("short-error.txt")
|
||||
.actual_text("(linker error)", out.stderr())
|
||||
- .normalize(r#"/rustc[^/]*/"#, "/rustc/")
|
||||
+ .normalize(r#"/tmp/rustc[^/]*/"#, "/tmp/rustc/")
|
||||
.normalize(
|
||||
regex::escape(run_make_support::build_root().to_str().unwrap()),
|
||||
"/build-root",
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2025-05-12"
|
||||
channel = "nightly-2025-05-21"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ pub(crate) unsafe fn codegen(
|
|||
let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name));
|
||||
let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name));
|
||||
|
||||
create_wrapper_function(tcx, context, &from_name, &to_name, &types, output);
|
||||
create_wrapper_function(tcx, context, &from_name, Some(&to_name), &types, output);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ pub(crate) unsafe fn codegen(
|
|||
tcx,
|
||||
context,
|
||||
&mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
|
||||
&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)),
|
||||
Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))),
|
||||
&[usize, usize],
|
||||
None,
|
||||
);
|
||||
|
|
@ -81,21 +81,21 @@ pub(crate) unsafe fn codegen(
|
|||
let value = context.new_rvalue_from_int(i8, value as i32);
|
||||
global.global_set_initializer_rvalue(value);
|
||||
|
||||
let name = mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE);
|
||||
let global = context.new_global(None, GlobalKind::Exported, i8, name);
|
||||
#[cfg(feature = "master")]
|
||||
global.add_attribute(VarAttribute::Visibility(symbol_visibility_to_gcc(
|
||||
tcx.sess.default_visibility(),
|
||||
)));
|
||||
let value = context.new_rvalue_from_int(i8, 0);
|
||||
global.global_set_initializer_rvalue(value);
|
||||
create_wrapper_function(
|
||||
tcx,
|
||||
context,
|
||||
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
fn create_wrapper_function(
|
||||
tcx: TyCtxt<'_>,
|
||||
context: &Context<'_>,
|
||||
from_name: &str,
|
||||
to_name: &str,
|
||||
to_name: Option<&str>,
|
||||
types: &[Type<'_>],
|
||||
output: Option<Type<'_>>,
|
||||
) {
|
||||
|
|
@ -124,34 +124,40 @@ fn create_wrapper_function(
|
|||
// TODO(antoyo): emit unwind tables.
|
||||
}
|
||||
|
||||
let args: Vec<_> = types
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, typ)| context.new_parameter(None, *typ, format!("param{}", index)))
|
||||
.collect();
|
||||
let callee = context.new_function(
|
||||
None,
|
||||
FunctionType::Extern,
|
||||
output.unwrap_or(void),
|
||||
&args,
|
||||
to_name,
|
||||
false,
|
||||
);
|
||||
#[cfg(feature = "master")]
|
||||
callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
|
||||
|
||||
let block = func.new_block("entry");
|
||||
|
||||
let args = args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, _)| func.get_param(i as i32).to_rvalue())
|
||||
.collect::<Vec<_>>();
|
||||
let ret = context.new_call(None, callee, &args);
|
||||
//llvm::LLVMSetTailCall(ret, True);
|
||||
if output.is_some() {
|
||||
block.end_with_return(None, ret);
|
||||
if let Some(to_name) = to_name {
|
||||
let args: Vec<_> = types
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, typ)| context.new_parameter(None, *typ, format!("param{}", index)))
|
||||
.collect();
|
||||
let callee = context.new_function(
|
||||
None,
|
||||
FunctionType::Extern,
|
||||
output.unwrap_or(void),
|
||||
&args,
|
||||
to_name,
|
||||
false,
|
||||
);
|
||||
#[cfg(feature = "master")]
|
||||
callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
|
||||
|
||||
let args = args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, _)| func.get_param(i as i32).to_rvalue())
|
||||
.collect::<Vec<_>>();
|
||||
let ret = context.new_call(None, callee, &args);
|
||||
//llvm::LLVMSetTailCall(ret, True);
|
||||
if output.is_some() {
|
||||
block.end_with_return(None, ret);
|
||||
} else {
|
||||
block.add_eval(None, ret);
|
||||
block.end_with_void_return(None);
|
||||
}
|
||||
} else {
|
||||
assert!(output.is_none());
|
||||
block.end_with_void_return(None);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
// cSpell:ignoreRegExp [afkspqvwy]reg
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use gccjit::{LValue, RValue, ToRValue, Type};
|
||||
|
|
@ -138,7 +140,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
// `outputs.len() + inputs.len()`.
|
||||
let mut labels = vec![];
|
||||
|
||||
// Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
|
||||
// Clobbers collected from `out("explicit register") _` and `inout("explicit_reg") var => _`
|
||||
let mut clobbers = vec![];
|
||||
|
||||
// We're trying to preallocate space for the template
|
||||
|
|
@ -203,7 +205,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
// is also used as an in register, do not add it to the clobbers list.
|
||||
// it will be treated as a lateout register with `out_place: None`
|
||||
if !late {
|
||||
bug!("input registers can only be used as lateout regisers");
|
||||
bug!("input registers can only be used as lateout registers");
|
||||
}
|
||||
("r", dummy_output_type(self.cx, reg.reg_class()))
|
||||
} else {
|
||||
|
|
@ -641,7 +643,8 @@ fn explicit_reg_to_gcc(reg: InlineAsmReg) -> &'static str {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
InlineAsmReg::Arm(reg) => reg.name(),
|
||||
InlineAsmReg::AArch64(reg) => reg.name(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use crate::gcc_util::to_gcc_features;
|
|||
/// Checks if the function `instance` is recursively inline.
|
||||
/// Returns `false` if a functions is guaranteed to be non-recursive, and `true` if it *might* be recursive.
|
||||
#[cfg(feature = "master")]
|
||||
fn resursively_inline<'gcc, 'tcx>(
|
||||
fn recursively_inline<'gcc, 'tcx>(
|
||||
cx: &CodegenCx<'gcc, 'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
) -> bool {
|
||||
|
|
@ -61,7 +61,7 @@ fn inline_attr<'gcc, 'tcx>(
|
|||
//
|
||||
// That prevents issues steming from recursive `#[inline(always)]` at a *relatively* small cost.
|
||||
// We *only* need to check all the terminators of a function marked with this attribute.
|
||||
if resursively_inline(cx, instance) {
|
||||
if recursively_inline(cx, instance) {
|
||||
Some(FnAttribute::Inline)
|
||||
} else {
|
||||
Some(FnAttribute::AlwaysInline)
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@
|
|||
// does not remove it?
|
||||
//
|
||||
// TODO(antoyo): for performance, check which optimizations the C++ frontend enables.
|
||||
//
|
||||
// cSpell:disable
|
||||
// Fix these warnings:
|
||||
// /usr/bin/ld: warning: type of symbol `_RNvNvNvNtCs5JWOrf9uCus_5rayon11thread_pool19WORKER_THREAD_STATE7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
|
||||
// /usr/bin/ld: warning: type of symbol `_RNvNvNvNvNtNtNtCsAj5i4SGTR7_3std4sync4mpmc5waker17current_thread_id5DUMMY7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
|
||||
// /usr/bin/ld: warning: incremental linking of LTO and non-LTO objects; using -flinker-output=nolto-rel which will bypass whole program optimization
|
||||
// cSpell:enable
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fs::{self, File};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
|
|||
|
|
@ -186,6 +186,7 @@ pub(crate) fn codegen(
|
|||
|
||||
if fat_lto {
|
||||
let lto_path = format!("{}.lto", path);
|
||||
// cSpell:disable
|
||||
// FIXME(antoyo): The LTO frontend generates the following warning:
|
||||
// ../build_sysroot/sysroot_src/library/core/src/num/dec2flt/lemire.rs:150:15: warning: type of ‘_ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E’ does not match original declaration [-Wlto-type-mismatch]
|
||||
// 150 | let (lo5, hi5) = POWER_OF_FIVE_128[index];
|
||||
|
|
@ -193,6 +194,7 @@ pub(crate) fn codegen(
|
|||
// lto1: note: ‘_ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E’ was previously declared here
|
||||
//
|
||||
// This option is to mute it to make the UI tests pass with LTO enabled.
|
||||
// cSpell:enable
|
||||
context.add_driver_option("-Wno-lto-type-mismatch");
|
||||
// NOTE: this doesn't actually generate an executable. With the above
|
||||
// flags, it combines the .o files together in another .o.
|
||||
|
|
|
|||
|
|
@ -765,7 +765,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
|
||||
#[cfg(feature = "master")]
|
||||
match self.cx.type_kind(a_type) {
|
||||
TypeKind::Half | TypeKind::Float => {
|
||||
TypeKind::Half => {
|
||||
let fmodf = self.context.get_builtin_function("fmodf");
|
||||
let f32_type = self.type_f32();
|
||||
let a = self.context.new_cast(self.location, a, f32_type);
|
||||
let b = self.context.new_cast(self.location, b, f32_type);
|
||||
let result = self.context.new_call(self.location, fmodf, &[a, b]);
|
||||
return self.context.new_cast(self.location, result, a_type);
|
||||
}
|
||||
TypeKind::Float => {
|
||||
let fmodf = self.context.get_builtin_function("fmodf");
|
||||
return self.context.new_call(self.location, fmodf, &[a, b]);
|
||||
}
|
||||
|
|
@ -774,8 +782,19 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
return self.context.new_call(self.location, fmod, &[a, b]);
|
||||
}
|
||||
TypeKind::FP128 => {
|
||||
let fmodl = self.context.get_builtin_function("fmodl");
|
||||
return self.context.new_call(self.location, fmodl, &[a, b]);
|
||||
let f128_type = self.type_f128();
|
||||
let fmodf128 = self.context.new_function(
|
||||
None,
|
||||
gccjit::FunctionType::Extern,
|
||||
f128_type,
|
||||
&[
|
||||
self.context.new_parameter(None, f128_type, "a"),
|
||||
self.context.new_parameter(None, f128_type, "b"),
|
||||
],
|
||||
"fmodf128",
|
||||
false,
|
||||
);
|
||||
return self.context.new_call(self.location, fmodf128, &[a, b]);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -924,7 +943,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
// dereference after a drop, for instance.
|
||||
// FIXME(antoyo): this check that we don't call get_aligned() a second time on a type.
|
||||
// Ideally, we shouldn't need to do this check.
|
||||
let aligned_type = if pointee_ty == self.cx.u128_type || pointee_ty == self.cx.i128_type {
|
||||
// FractalFir: the `align == self.int128_align` check ensures we *do* call `get_aligned` if
|
||||
// the alignment of a `u128`/`i128` is not the one mandated by the ABI. This ensures we handle
|
||||
// under-aligned loads correctly.
|
||||
let aligned_type = if (pointee_ty == self.cx.u128_type || pointee_ty == self.cx.i128_type)
|
||||
&& align == self.int128_align
|
||||
{
|
||||
pointee_ty
|
||||
} else {
|
||||
pointee_ty.get_aligned(align.bytes())
|
||||
|
|
@ -1010,13 +1034,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
let b_offset = a.size(self).align_to(b.align(self).abi);
|
||||
|
||||
let mut load = |i, scalar: &abi::Scalar, align| {
|
||||
let llptr = if i == 0 {
|
||||
let ptr = if i == 0 {
|
||||
place.val.llval
|
||||
} else {
|
||||
self.inbounds_ptradd(place.val.llval, self.const_usize(b_offset.bytes()))
|
||||
};
|
||||
let llty = place.layout.scalar_pair_element_gcc_type(self, i);
|
||||
let load = self.load(llty, llptr, align);
|
||||
let load = self.load(llty, ptr, align);
|
||||
scalar_load_metadata(self, load, scalar);
|
||||
if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
|
|||
unreachable!();
|
||||
/*
|
||||
// Create a fn pointer with the new signature.
|
||||
let ptrty = fn_abi.ptr_to_gcc_type(cx);
|
||||
let ptrtype = fn_abi.ptr_to_gcc_type(cx);
|
||||
|
||||
// This is subtle and surprising, but sometimes we have to bitcast
|
||||
// the resulting fn pointer. The reason has to do with external
|
||||
|
|
@ -59,7 +59,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
|
|||
// This can occur on either a crate-local or crate-external
|
||||
// reference. It also occurs when testing libcore and in some
|
||||
// other weird situations. Annoying.
|
||||
if cx.val_ty(func) != ptrty {
|
||||
if cx.val_ty(func) != ptrtype {
|
||||
// TODO(antoyo): cast the pointer.
|
||||
func
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use rustc_middle::mir::Mutability;
|
|||
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
|
||||
use crate::consts::const_alloc_to_gcc;
|
||||
use crate::context::CodegenCx;
|
||||
use crate::type_of::LayoutGccExt;
|
||||
|
||||
|
|
@ -46,12 +45,65 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
}
|
||||
|
||||
pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> {
|
||||
let context = &cx.context;
|
||||
let byte_type = context.new_type::<u8>();
|
||||
let typ = context.new_array_type(None, byte_type, bytes.len() as u64);
|
||||
let elements: Vec<_> =
|
||||
bytes.iter().map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32)).collect();
|
||||
context.new_array_constructor(None, typ, &elements)
|
||||
// Instead of always using an array of bytes, use an array of larger integers of target endianness
|
||||
// if possible. This reduces the amount of `rvalues` we use, which reduces memory usage significantly.
|
||||
//
|
||||
// FIXME(FractalFir): Consider using `global_set_initializer` instead. Before this is done, we need to confirm that
|
||||
// `global_set_initializer` is more memory efficient than the current solution.
|
||||
// `global_set_initializer` calls `global_set_initializer_rvalue` under the hood - does it generate an array of rvalues,
|
||||
// or is it using a more efficient representation?
|
||||
match bytes.len() % 8 {
|
||||
0 => {
|
||||
let context = &cx.context;
|
||||
let byte_type = context.new_type::<u64>();
|
||||
let typ = context.new_array_type(None, byte_type, bytes.len() as u64 / 8);
|
||||
let elements: Vec<_> = bytes
|
||||
.chunks_exact(8)
|
||||
.map(|arr| {
|
||||
let arr: [u8; 8] = arr.try_into().unwrap();
|
||||
context.new_rvalue_from_long(
|
||||
byte_type,
|
||||
// Since we are representing arbitrary byte runs as integers, we need to follow the target
|
||||
// endianness.
|
||||
match cx.sess().target.options.endian {
|
||||
rustc_abi::Endian::Little => u64::from_le_bytes(arr) as i64,
|
||||
rustc_abi::Endian::Big => u64::from_be_bytes(arr) as i64,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
context.new_array_constructor(None, typ, &elements)
|
||||
}
|
||||
4 => {
|
||||
let context = &cx.context;
|
||||
let byte_type = context.new_type::<u32>();
|
||||
let typ = context.new_array_type(None, byte_type, bytes.len() as u64 / 4);
|
||||
let elements: Vec<_> = bytes
|
||||
.chunks_exact(4)
|
||||
.map(|arr| {
|
||||
let arr: [u8; 4] = arr.try_into().unwrap();
|
||||
context.new_rvalue_from_int(
|
||||
byte_type,
|
||||
match cx.sess().target.options.endian {
|
||||
rustc_abi::Endian::Little => u32::from_le_bytes(arr) as i32,
|
||||
rustc_abi::Endian::Big => u32::from_be_bytes(arr) as i32,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
context.new_array_constructor(None, typ, &elements)
|
||||
}
|
||||
_ => {
|
||||
let context = cx.context;
|
||||
let byte_type = context.new_type::<u8>();
|
||||
let typ = context.new_array_type(None, byte_type, bytes.len() as u64);
|
||||
let elements: Vec<_> = bytes
|
||||
.iter()
|
||||
.map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32))
|
||||
.collect();
|
||||
context.new_array_constructor(None, typ, &elements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_is_pointer(typ: Type<'_>) -> bool {
|
||||
|
|
@ -185,14 +237,15 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
|
|||
|
||||
// FIXME(antoyo): there's some issues with using the u128 code that follows, so hard-code
|
||||
// the paths for floating-point values.
|
||||
if ty == self.float_type {
|
||||
// TODO: Remove this code?
|
||||
/*if ty == self.float_type {
|
||||
return self
|
||||
.context
|
||||
.new_rvalue_from_double(ty, f32::from_bits(data as u32) as f64);
|
||||
}
|
||||
if ty == self.double_type {
|
||||
return self.context.new_rvalue_from_double(ty, f64::from_bits(data as u64));
|
||||
}
|
||||
}*/
|
||||
|
||||
let value = self.const_uint_big(self.type_ix(bitsize), data);
|
||||
let bytesize = layout.size(self).bytes();
|
||||
|
|
@ -212,7 +265,20 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
|
|||
let alloc_id = prov.alloc_id();
|
||||
let base_addr = match self.tcx.global_alloc(alloc_id) {
|
||||
GlobalAlloc::Memory(alloc) => {
|
||||
let init = const_alloc_to_gcc(self, alloc);
|
||||
// For ZSTs directly codegen an aligned pointer.
|
||||
// This avoids generating a zero-sized constant value and actually needing a
|
||||
// real address at runtime.
|
||||
if alloc.inner().len() == 0 {
|
||||
assert_eq!(offset.bytes(), 0);
|
||||
let val = self.const_usize(alloc.inner().align.bytes());
|
||||
return if matches!(layout.primitive(), Pointer(_)) {
|
||||
self.context.new_cast(None, val, ty)
|
||||
} else {
|
||||
self.const_bitcast(val, ty)
|
||||
};
|
||||
}
|
||||
|
||||
let init = self.const_data_from_alloc(alloc);
|
||||
let alloc = alloc.inner();
|
||||
let value = match alloc.mutability {
|
||||
Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None),
|
||||
|
|
@ -234,7 +300,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
|
|||
}),
|
||||
)))
|
||||
.unwrap_memory();
|
||||
let init = const_alloc_to_gcc(self, alloc);
|
||||
let init = self.const_data_from_alloc(alloc);
|
||||
self.static_addr_of(init, alloc.inner().align, None)
|
||||
}
|
||||
GlobalAlloc::Static(def_id) => {
|
||||
|
|
@ -257,7 +323,19 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
|
|||
}
|
||||
|
||||
fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value {
|
||||
const_alloc_to_gcc(self, alloc)
|
||||
// We ignore the alignment for the purpose of deduping RValues
|
||||
// The alignment is not handled / used in any way by `const_alloc_to_gcc`,
|
||||
// so it is OK to overwrite it here.
|
||||
let mut mock_alloc = alloc.inner().clone();
|
||||
mock_alloc.align = rustc_abi::Align::MAX;
|
||||
// Check if the rvalue is already in the cache - if so, just return it directly.
|
||||
if let Some(res) = self.const_cache.borrow().get(&mock_alloc) {
|
||||
return *res;
|
||||
}
|
||||
// Rvalue not in the cache - convert and add it.
|
||||
let res = crate::consts::const_alloc_to_gcc_uncached(self, alloc);
|
||||
self.const_cache.borrow_mut().insert(mock_alloc, res);
|
||||
res
|
||||
}
|
||||
|
||||
fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {
|
||||
|
|
|
|||
|
|
@ -36,18 +36,14 @@ fn set_global_alignment<'gcc, 'tcx>(
|
|||
|
||||
impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> {
|
||||
fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> {
|
||||
// TODO(antoyo): implement a proper rvalue comparison in libgccjit instead of doing the
|
||||
// following:
|
||||
for (value, variable) in &*self.const_globals.borrow() {
|
||||
if format!("{:?}", value) == format!("{:?}", cv) {
|
||||
if let Some(global_variable) = self.global_lvalues.borrow().get(variable) {
|
||||
let alignment = align.bits() as i32;
|
||||
if alignment > global_variable.get_alignment() {
|
||||
global_variable.set_alignment(alignment);
|
||||
}
|
||||
if let Some(variable) = self.const_globals.borrow().get(&cv) {
|
||||
if let Some(global_variable) = self.global_lvalues.borrow().get(variable) {
|
||||
let alignment = align.bits() as i32;
|
||||
if alignment > global_variable.get_alignment() {
|
||||
global_variable.set_alignment(alignment);
|
||||
}
|
||||
return *variable;
|
||||
}
|
||||
return *variable;
|
||||
}
|
||||
let global_value = self.static_addr_of_mut(cv, align, kind);
|
||||
#[cfg(feature = "master")]
|
||||
|
|
@ -288,8 +284,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
global
|
||||
}
|
||||
}
|
||||
|
||||
pub fn const_alloc_to_gcc<'gcc>(
|
||||
/// Converts a given const alloc to a gcc Rvalue, without any caching or deduplication.
|
||||
/// YOU SHOULD NOT call this function directly - that may break the semantics of Rust.
|
||||
/// Use `const_data_from_alloc` instead.
|
||||
pub(crate) fn const_alloc_to_gcc_uncached<'gcc>(
|
||||
cx: &CodegenCx<'gcc, '_>,
|
||||
alloc: ConstAllocation<'_>,
|
||||
) -> RValue<'gcc> {
|
||||
|
|
@ -321,7 +319,7 @@ pub fn const_alloc_to_gcc<'gcc>(
|
|||
// and we properly interpret the provenance as a relocation pointer offset.
|
||||
alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)),
|
||||
)
|
||||
.expect("const_alloc_to_llvm: could not read relocation pointer")
|
||||
.expect("const_alloc_to_gcc_uncached: could not read relocation pointer")
|
||||
as u64;
|
||||
|
||||
let address_space = cx.tcx.global_alloc(alloc_id).address_space(cx);
|
||||
|
|
@ -360,7 +358,7 @@ fn codegen_static_initializer<'gcc, 'tcx>(
|
|||
def_id: DefId,
|
||||
) -> Result<(RValue<'gcc>, ConstAllocation<'tcx>), ErrorHandled> {
|
||||
let alloc = cx.tcx.eval_static_initializer(def_id)?;
|
||||
Ok((const_alloc_to_gcc(cx, alloc), alloc))
|
||||
Ok((cx.const_data_from_alloc(alloc), alloc))
|
||||
}
|
||||
|
||||
fn check_and_apply_linkage<'gcc, 'tcx>(
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use gccjit::{
|
||||
Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type,
|
||||
};
|
||||
use rustc_abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
|
||||
use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
|
||||
use rustc_codegen_ssa::base::wants_msvc_seh;
|
||||
use rustc_codegen_ssa::errors as ssa_errors;
|
||||
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods};
|
||||
use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_middle::mir::interpret::Allocation;
|
||||
use rustc_middle::mir::mono::CodegenUnit;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::layout::{
|
||||
|
|
@ -28,6 +30,8 @@ use crate::common::SignType;
|
|||
|
||||
#[cfg_attr(not(feature = "master"), allow(dead_code))]
|
||||
pub struct CodegenCx<'gcc, 'tcx> {
|
||||
/// A cache of converted ConstAllocs
|
||||
pub const_cache: RefCell<HashMap<Allocation, RValue<'gcc>>>,
|
||||
pub codegen_unit: &'tcx CodegenUnit<'tcx>,
|
||||
pub context: &'gcc Context<'gcc>,
|
||||
|
||||
|
|
@ -129,6 +133,9 @@ pub struct CodegenCx<'gcc, 'tcx> {
|
|||
|
||||
#[cfg(feature = "master")]
|
||||
pub cleanup_blocks: RefCell<FxHashSet<Block<'gcc>>>,
|
||||
/// The alignment of a u128/i128 type.
|
||||
// We cache this, since it is needed for alignment checks during loads.
|
||||
pub int128_align: Align,
|
||||
}
|
||||
|
||||
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
||||
|
|
@ -220,6 +227,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
}
|
||||
|
||||
let mut cx = Self {
|
||||
int128_align: tcx
|
||||
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tcx.types.i128))
|
||||
.expect("Can't get the layout of `i128`")
|
||||
.align
|
||||
.abi,
|
||||
const_cache: Default::default(),
|
||||
codegen_unit,
|
||||
context,
|
||||
current_func: RefCell::new(None),
|
||||
|
|
@ -428,8 +441,8 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
|||
// `rust_eh_personality` function, but rather we wired it up to the
|
||||
// CRT's custom personality function, which forces LLVM to consider
|
||||
// landing pads as "landing pads for SEH".
|
||||
if let Some(llpersonality) = self.eh_personality.get() {
|
||||
return llpersonality;
|
||||
if let Some(personality_func) = self.eh_personality.get() {
|
||||
return personality_func;
|
||||
}
|
||||
let tcx = self.tcx;
|
||||
let func = match tcx.lang_items().eh_personality() {
|
||||
|
|
|
|||
|
|
@ -1,30 +1,6 @@
|
|||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_macros::Diagnostic;
|
||||
use rustc_span::Span;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_unknown_ctarget_feature_prefix)]
|
||||
#[note]
|
||||
pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
|
||||
pub feature: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_unknown_ctarget_feature)]
|
||||
#[note]
|
||||
pub(crate) struct UnknownCTargetFeature<'a> {
|
||||
pub feature: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub rust_feature: PossibleFeature<'a>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum PossibleFeature<'a> {
|
||||
#[help(codegen_gcc_possible_feature)]
|
||||
Some { rust_feature: &'a str },
|
||||
#[help(codegen_gcc_consider_filing_feature_request)]
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_unwinding_inline_asm)]
|
||||
pub(crate) struct UnwindingInlineAsm {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
#[cfg(feature = "master")]
|
||||
use gccjit::Context;
|
||||
use rustc_codegen_ssa::codegen_attrs::check_tied_features;
|
||||
use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_codegen_ssa::target_features;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
|
||||
use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
|
||||
use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
|
||||
|
||||
fn gcc_features_by_flags(sess: &Session) -> Vec<&str> {
|
||||
let mut features: Vec<&str> = Vec::new();
|
||||
retpoline_features_by_flags(sess, &mut features);
|
||||
features
|
||||
fn gcc_features_by_flags(sess: &Session, features: &mut Vec<String>) {
|
||||
target_features::retpoline_features_by_flags(sess, features);
|
||||
// FIXME: LLVM also sets +reserve-x18 here under some conditions.
|
||||
}
|
||||
|
||||
/// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
|
||||
|
|
@ -44,98 +36,29 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
|
|||
features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from));
|
||||
|
||||
// -Ctarget-features
|
||||
let known_features = sess.target.rust_target_features();
|
||||
let mut featsmap = FxHashMap::default();
|
||||
|
||||
// Compute implied features
|
||||
let mut all_rust_features = vec![];
|
||||
for feature in sess.opts.cg.target_feature.split(',').chain(gcc_features_by_flags(sess)) {
|
||||
if let Some(feature) = feature.strip_prefix('+') {
|
||||
all_rust_features.extend(
|
||||
UnordSet::from(sess.target.implied_target_features(feature))
|
||||
.to_sorted_stable_ord()
|
||||
.iter()
|
||||
.map(|&&s| (true, s)),
|
||||
)
|
||||
} else if let Some(feature) = feature.strip_prefix('-') {
|
||||
// FIXME: Why do we not remove implied features on "-" here?
|
||||
// We do the equivalent above in `target_config`.
|
||||
// See <https://github.com/rust-lang/rust/issues/134792>.
|
||||
all_rust_features.push((false, feature));
|
||||
} else if !feature.is_empty() && diagnostics {
|
||||
sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
|
||||
}
|
||||
}
|
||||
// Remove features that are meant for rustc, not codegen.
|
||||
all_rust_features.retain(|&(_, feature)| {
|
||||
// Retain if it is not a rustc feature
|
||||
!RUSTC_SPECIFIC_FEATURES.contains(&feature)
|
||||
});
|
||||
|
||||
// Check feature validity.
|
||||
if diagnostics {
|
||||
for &(enable, feature) in &all_rust_features {
|
||||
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
|
||||
match feature_state {
|
||||
None => {
|
||||
let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| {
|
||||
let gcc_features = to_gcc_features(sess, rust_feature);
|
||||
if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature)
|
||||
{
|
||||
Some(rust_feature)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let unknown_feature = if let Some(rust_feature) = rust_feature {
|
||||
UnknownCTargetFeature {
|
||||
feature,
|
||||
rust_feature: PossibleFeature::Some { rust_feature },
|
||||
}
|
||||
} else {
|
||||
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
|
||||
};
|
||||
sess.dcx().emit_warn(unknown_feature);
|
||||
}
|
||||
Some(&(_, stability, _)) => {
|
||||
stability.verify_feature_enabled_by_flag(sess, enable, feature);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(nagisa): figure out how to not allocate a full hashset here.
|
||||
featsmap.insert(feature, enable);
|
||||
}
|
||||
}
|
||||
|
||||
// Translate this into GCC features.
|
||||
let feats =
|
||||
all_rust_features.iter().flat_map(|&(enable, feature)| {
|
||||
let enable_disable = if enable { '+' } else { '-' };
|
||||
target_features::flag_to_backend_features(
|
||||
sess,
|
||||
diagnostics,
|
||||
|feature| to_gcc_features(sess, feature),
|
||||
|feature, enable| {
|
||||
// We run through `to_gcc_features` when
|
||||
// passing requests down to GCC. This means that all in-language
|
||||
// features also work on the command line instead of having two
|
||||
// different names when the GCC name and the Rust name differ.
|
||||
to_gcc_features(sess, feature)
|
||||
.iter()
|
||||
.flat_map(|feat| to_gcc_features(sess, feat).into_iter())
|
||||
.map(|feature| {
|
||||
if enable_disable == '-' {
|
||||
format!("-{}", feature)
|
||||
} else {
|
||||
feature.to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
features.extend(feats);
|
||||
features.extend(
|
||||
to_gcc_features(sess, feature)
|
||||
.iter()
|
||||
.flat_map(|feat| to_gcc_features(sess, feat).into_iter())
|
||||
.map(
|
||||
|feature| {
|
||||
if !enable { format!("-{}", feature) } else { feature.to_string() }
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
|
||||
sess.dcx().emit_err(TargetFeatureDisableOrEnable {
|
||||
features: f,
|
||||
span: None,
|
||||
missing_features: None,
|
||||
});
|
||||
}
|
||||
gcc_features_by_flags(sess, &mut features);
|
||||
|
||||
features
|
||||
}
|
||||
|
|
@ -143,6 +66,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
|
|||
// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
|
||||
pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
|
||||
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
|
||||
// cSpell:disable
|
||||
match (arch, s) {
|
||||
// FIXME: seems like x87 does not exist?
|
||||
("x86", "x87") => smallvec![],
|
||||
|
|
@ -181,6 +105,7 @@ pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]>
|
|||
("aarch64", "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"],
|
||||
(_, s) => smallvec![s],
|
||||
}
|
||||
// cSpell:enable
|
||||
}
|
||||
|
||||
fn arch_to_gcc(name: &str) -> &str {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
//! This module exists because some integer types are not supported on some gcc platforms, e.g.
|
||||
//! 128-bit integers on 32-bit platforms and thus require to be handled manually.
|
||||
|
||||
// cSpell:words cmpti divti modti mulodi muloti udivti umodti
|
||||
|
||||
use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp};
|
||||
use rustc_abi::{CanonAbi, Endian, ExternAbi};
|
||||
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
||||
|
|
@ -913,9 +915,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
|
||||
debug_assert!(value_type.dyncast_array().is_some());
|
||||
let name_suffix = match self.type_kind(dest_typ) {
|
||||
// cSpell:disable
|
||||
TypeKind::Float => "tisf",
|
||||
TypeKind::Double => "tidf",
|
||||
TypeKind::FP128 => "tixf",
|
||||
TypeKind::FP128 => "titf",
|
||||
// cSpell:enable
|
||||
kind => panic!("cannot cast a non-native integer to type {:?}", kind),
|
||||
};
|
||||
let sign = if signed { "" } else { "un" };
|
||||
|
|
@ -957,8 +961,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
|
||||
debug_assert!(dest_typ.dyncast_array().is_some());
|
||||
let name_suffix = match self.type_kind(value_type) {
|
||||
// cSpell:disable
|
||||
TypeKind::Float => "sfti",
|
||||
TypeKind::Double => "dfti",
|
||||
// cSpell:enable
|
||||
kind => panic!("cannot cast a {:?} to non-native integer", kind),
|
||||
};
|
||||
let sign = if signed { "" } else { "uns" };
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1012,7 +1012,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
|
|||
};
|
||||
let func = cx.context.get_builtin_function(gcc_name);
|
||||
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
|
||||
return func;
|
||||
func
|
||||
}
|
||||
|
||||
#[cfg(feature = "master")]
|
||||
|
|
@ -1548,10 +1548,13 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
|
|||
"llvm.x86.tcmmrlfp16ps" => "__builtin_trap",
|
||||
|
||||
// NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py
|
||||
_ => include!("archs.rs"),
|
||||
_ => map_arch_intrinsic(name),
|
||||
};
|
||||
|
||||
let func = cx.context.get_target_builtin_function(gcc_name);
|
||||
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
|
||||
func
|
||||
}
|
||||
|
||||
#[cfg(feature = "master")]
|
||||
include!("archs.rs");
|
||||
|
|
|
|||
|
|
@ -196,6 +196,95 @@ fn get_simple_function<'gcc, 'tcx>(
|
|||
))
|
||||
}
|
||||
|
||||
fn get_simple_function_f128<'gcc, 'tcx>(
|
||||
cx: &CodegenCx<'gcc, 'tcx>,
|
||||
name: Symbol,
|
||||
) -> Option<Function<'gcc>> {
|
||||
if !cx.supports_f128_type {
|
||||
return None;
|
||||
}
|
||||
|
||||
let f128_type = cx.type_f128();
|
||||
let func_name = match name {
|
||||
sym::ceilf128 => "ceilf128",
|
||||
sym::floorf128 => "floorf128",
|
||||
sym::truncf128 => "truncf128",
|
||||
sym::roundf128 => "roundf128",
|
||||
sym::round_ties_even_f128 => "roundevenf128",
|
||||
sym::sqrtf128 => "sqrtf128",
|
||||
_ => return None,
|
||||
};
|
||||
Some(cx.context.new_function(
|
||||
None,
|
||||
FunctionType::Extern,
|
||||
f128_type,
|
||||
&[cx.context.new_parameter(None, f128_type, "a")],
|
||||
func_name,
|
||||
false,
|
||||
))
|
||||
}
|
||||
|
||||
fn get_simple_function_f128_2args<'gcc, 'tcx>(
|
||||
cx: &CodegenCx<'gcc, 'tcx>,
|
||||
name: Symbol,
|
||||
) -> Option<Function<'gcc>> {
|
||||
if !cx.supports_f128_type {
|
||||
return None;
|
||||
}
|
||||
|
||||
let f128_type = cx.type_f128();
|
||||
let func_name = match name {
|
||||
sym::maxnumf128 => "fmaxf128",
|
||||
sym::minnumf128 => "fminf128",
|
||||
_ => return None,
|
||||
};
|
||||
Some(cx.context.new_function(
|
||||
None,
|
||||
FunctionType::Extern,
|
||||
f128_type,
|
||||
&[
|
||||
cx.context.new_parameter(None, f128_type, "a"),
|
||||
cx.context.new_parameter(None, f128_type, "b"),
|
||||
],
|
||||
func_name,
|
||||
false,
|
||||
))
|
||||
}
|
||||
|
||||
fn f16_builtin<'gcc, 'tcx>(
|
||||
cx: &CodegenCx<'gcc, 'tcx>,
|
||||
name: Symbol,
|
||||
args: &[OperandRef<'tcx, RValue<'gcc>>],
|
||||
) -> RValue<'gcc> {
|
||||
let f32_type = cx.type_f32();
|
||||
let builtin_name = match name {
|
||||
sym::ceilf16 => "__builtin_ceilf",
|
||||
sym::floorf16 => "__builtin_floorf",
|
||||
sym::fmaf16 => "fmaf",
|
||||
sym::maxnumf16 => "__builtin_fmaxf",
|
||||
sym::minnumf16 => "__builtin_fminf",
|
||||
sym::powf16 => "__builtin_powf",
|
||||
sym::powif16 => {
|
||||
let func = cx.context.get_builtin_function("__builtin_powif");
|
||||
let arg0 = cx.context.new_cast(None, args[0].immediate(), f32_type);
|
||||
let args = [arg0, args[1].immediate()];
|
||||
let result = cx.context.new_call(None, func, &args);
|
||||
return cx.context.new_cast(None, result, cx.type_f16());
|
||||
}
|
||||
sym::roundf16 => "__builtin_roundf",
|
||||
sym::round_ties_even_f16 => "__builtin_rintf",
|
||||
sym::sqrtf16 => "__builtin_sqrtf",
|
||||
sym::truncf16 => "__builtin_truncf",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let func = cx.context.get_builtin_function(builtin_name);
|
||||
let args: Vec<_> =
|
||||
args.iter().map(|arg| cx.context.new_cast(None, arg.immediate(), f32_type)).collect();
|
||||
let result = cx.context.new_call(None, func, &args);
|
||||
cx.context.new_cast(None, result, cx.type_f16())
|
||||
}
|
||||
|
||||
impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
fn codegen_intrinsic_call(
|
||||
&mut self,
|
||||
|
|
@ -211,7 +300,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
let fn_args = instance.args;
|
||||
|
||||
let simple = get_simple_intrinsic(self, name);
|
||||
let simple_func = get_simple_function(self, name);
|
||||
let simple_func = get_simple_function(self, name)
|
||||
.or_else(|| get_simple_function_f128(self, name))
|
||||
.or_else(|| get_simple_function_f128_2args(self, name));
|
||||
|
||||
// FIXME(tempdragon): Re-enable `clippy::suspicious_else_formatting` if the following issue is solved:
|
||||
// https://github.com/rust-lang/rust-clippy/issues/12497
|
||||
|
|
@ -234,17 +325,55 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
sym::fmaf16 => {
|
||||
// TODO(antoyo): use the correct builtin for f16.
|
||||
let func = self.cx.context.get_builtin_function("fmaf");
|
||||
let args: Vec<_> = args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
self.cx.context.new_cast(self.location, arg.immediate(), self.cx.type_f32())
|
||||
})
|
||||
.collect();
|
||||
let result = self.cx.context.new_call(self.location, func, &args);
|
||||
self.cx.context.new_cast(self.location, result, self.cx.type_f16())
|
||||
sym::ceilf16
|
||||
| sym::floorf16
|
||||
| sym::fmaf16
|
||||
| sym::maxnumf16
|
||||
| sym::minnumf16
|
||||
| sym::powf16
|
||||
| sym::powif16
|
||||
| sym::roundf16
|
||||
| sym::round_ties_even_f16
|
||||
| sym::sqrtf16
|
||||
| sym::truncf16 => f16_builtin(self, name, args),
|
||||
sym::fmaf128 => {
|
||||
let f128_type = self.cx.type_f128();
|
||||
let func = self.cx.context.new_function(
|
||||
None,
|
||||
FunctionType::Extern,
|
||||
f128_type,
|
||||
&[
|
||||
self.cx.context.new_parameter(None, f128_type, "a"),
|
||||
self.cx.context.new_parameter(None, f128_type, "b"),
|
||||
self.cx.context.new_parameter(None, f128_type, "c"),
|
||||
],
|
||||
"fmaf128",
|
||||
false,
|
||||
);
|
||||
self.cx.context.new_call(
|
||||
self.location,
|
||||
func,
|
||||
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
sym::powif128 => {
|
||||
let f128_type = self.cx.type_f128();
|
||||
let func = self.cx.context.new_function(
|
||||
None,
|
||||
FunctionType::Extern,
|
||||
f128_type,
|
||||
&[
|
||||
self.cx.context.new_parameter(None, f128_type, "a"),
|
||||
self.cx.context.new_parameter(None, self.int_type, "b"),
|
||||
],
|
||||
"__powitf2",
|
||||
false,
|
||||
);
|
||||
self.cx.context.new_call(
|
||||
self.location,
|
||||
func,
|
||||
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
sym::is_val_statically_known => {
|
||||
let a = args[0].immediate();
|
||||
|
|
@ -526,7 +655,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
|
||||
fn type_checked_load(
|
||||
&mut self,
|
||||
_llvtable: Self::Value,
|
||||
_vtable: Self::Value,
|
||||
_vtable_byte_offset: u64,
|
||||
_typeid: Self::Value,
|
||||
) -> Self::Value {
|
||||
|
|
@ -622,23 +751,23 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
|
|||
// We instead thus allocate some scratch space...
|
||||
let scratch_size = cast.size(bx);
|
||||
let scratch_align = cast.align(bx);
|
||||
let llscratch = bx.alloca(scratch_size, scratch_align);
|
||||
bx.lifetime_start(llscratch, scratch_size);
|
||||
let scratch = bx.alloca(scratch_size, scratch_align);
|
||||
bx.lifetime_start(scratch, scratch_size);
|
||||
|
||||
// ... where we first store the value...
|
||||
rustc_codegen_ssa::mir::store_cast(bx, cast, val, llscratch, scratch_align);
|
||||
rustc_codegen_ssa::mir::store_cast(bx, cast, val, scratch, scratch_align);
|
||||
|
||||
// ... and then memcpy it to the intended destination.
|
||||
bx.memcpy(
|
||||
dst.val.llval,
|
||||
self.layout.align.abi,
|
||||
llscratch,
|
||||
scratch,
|
||||
scratch_align,
|
||||
bx.const_usize(self.layout.size.bytes()),
|
||||
MemFlags::empty(),
|
||||
);
|
||||
|
||||
bx.lifetime_end(llscratch, scratch_size);
|
||||
bx.lifetime_end(scratch, scratch_size);
|
||||
}
|
||||
} else {
|
||||
OperandValue::Immediate(val).store(bx, dst);
|
||||
|
|
|
|||
|
|
@ -1081,7 +1081,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
|
|||
let (_, element_ty1) = args[1].layout.ty.simd_size_and_type(bx.tcx());
|
||||
let (_, element_ty2) = args[2].layout.ty.simd_size_and_type(bx.tcx());
|
||||
let (pointer_count, underlying_ty) = match *element_ty1.kind() {
|
||||
ty::RawPtr(p_ty, mutbl) if p_ty == in_elem && mutbl == hir::Mutability::Mut => {
|
||||
ty::RawPtr(p_ty, mutability)
|
||||
if p_ty == in_elem && mutability == hir::Mutability::Mut =>
|
||||
{
|
||||
(ptr_count(element_ty1), non_ptr(element_ty1))
|
||||
}
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
* TODO(antoyo): support #[inline] attributes.
|
||||
* TODO(antoyo): support LTO (gcc's equivalent to Full LTO is -flto -flto-partition=one — https://documentation.suse.com/sbp/all/html/SBP-GCC-10/index.html).
|
||||
* For Thin LTO, this might be helpful:
|
||||
// cspell:disable-next-line
|
||||
* In gcc 4.6 -fwhopr was removed and became default with -flto. The non-whopr path can still be executed via -flto-partition=none.
|
||||
* Or the new incremental LTO (https://www.phoronix.com/news/GCC-Incremental-LTO-Patches)?
|
||||
*
|
||||
* Maybe some missing optizations enabled by rustc's LTO is in there: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
|
||||
* Maybe some missing optimizations enabled by rustc's LTO is in there: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
|
||||
// cspell:disable-next-line
|
||||
* Like -fipa-icf (should be already enabled) and maybe -fdevirtualize-at-ltrans.
|
||||
* TODO: disable debug info always being emitted. Perhaps this slows down things?
|
||||
*
|
||||
|
|
@ -100,6 +102,7 @@ use rustc_codegen_ssa::back::write::{
|
|||
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn,
|
||||
};
|
||||
use rustc_codegen_ssa::base::codegen_crate;
|
||||
use rustc_codegen_ssa::target_features::cfg_target_feature;
|
||||
use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods};
|
||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
|
|
@ -206,7 +209,7 @@ impl CodegenBackend for GccCodegenBackend {
|
|||
#[cfg(not(feature = "master"))]
|
||||
{
|
||||
let temp_dir = TempDir::new().expect("cannot create temporary directory");
|
||||
let temp_file = temp_dir.into_path().join("result.asm");
|
||||
let temp_file = temp_dir.keep().join("result.asm");
|
||||
let check_context = Context::default();
|
||||
check_context.set_print_errors_to_stderr(false);
|
||||
let _int128_ty = check_context.new_c_type(CType::UInt128t);
|
||||
|
|
@ -430,10 +433,11 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
||||
back::write::link(cgcx, dcx, modules)
|
||||
}
|
||||
|
||||
fn autodiff(
|
||||
_cgcx: &CodegenContext<Self>,
|
||||
_module: &ModuleCodegen<Self::Module>,
|
||||
_diff_fncs: Vec<AutoDiffItem>,
|
||||
_diff_functions: Vec<AutoDiffItem>,
|
||||
_config: &ModuleConfig,
|
||||
) -> Result<(), FatalError> {
|
||||
unimplemented!()
|
||||
|
|
@ -473,48 +477,32 @@ fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
|
|||
|
||||
/// Returns the features that should be set in `cfg(target_feature)`.
|
||||
fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig {
|
||||
// TODO(antoyo): use global_gcc_features.
|
||||
let f = |allow_unstable| {
|
||||
sess.target
|
||||
.rust_target_features()
|
||||
.iter()
|
||||
.filter_map(|&(feature, gate, _)| {
|
||||
if allow_unstable
|
||||
|| (gate.in_cfg()
|
||||
&& (sess.is_nightly_build() || gate.requires_nightly().is_none()))
|
||||
{
|
||||
Some(feature)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter(|feature| {
|
||||
// TODO: we disable Neon for now since we don't support the LLVM intrinsics for it.
|
||||
if *feature == "neon" {
|
||||
return false;
|
||||
}
|
||||
target_info.cpu_supports(feature)
|
||||
/*
|
||||
adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
|
||||
avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
|
||||
bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
|
||||
sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
|
||||
*/
|
||||
})
|
||||
.map(Symbol::intern)
|
||||
.collect()
|
||||
};
|
||||
let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| {
|
||||
// TODO: we disable Neon for now since we don't support the LLVM intrinsics for it.
|
||||
if feature == "neon" {
|
||||
return false;
|
||||
}
|
||||
target_info.cpu_supports(feature)
|
||||
// cSpell:disable
|
||||
/*
|
||||
adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
|
||||
avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
|
||||
bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
|
||||
sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
|
||||
*/
|
||||
// cSpell:enable
|
||||
});
|
||||
|
||||
let target_features = f(false);
|
||||
let unstable_target_features = f(true);
|
||||
let has_reliable_f16 = target_info.supports_target_dependent_type(CType::Float16);
|
||||
let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128);
|
||||
|
||||
TargetConfig {
|
||||
target_features,
|
||||
unstable_target_features,
|
||||
// There are no known bugs with GCC support for f16 or f128
|
||||
has_reliable_f16: true,
|
||||
has_reliable_f16_math: true,
|
||||
has_reliable_f128: true,
|
||||
has_reliable_f128_math: true,
|
||||
has_reliable_f16,
|
||||
has_reliable_f16_math: has_reliable_f16,
|
||||
has_reliable_f128,
|
||||
has_reliable_f128_math: has_reliable_f128,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,13 +302,13 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> {
|
|||
#[cfg_attr(feature = "master", allow(unused_mut))]
|
||||
fn type_array(&self, ty: Type<'gcc>, mut len: u64) -> Type<'gcc> {
|
||||
#[cfg(not(feature = "master"))]
|
||||
if let Some(struct_type) = ty.is_struct() {
|
||||
if struct_type.get_field_count() == 0 {
|
||||
// NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a
|
||||
// size of usize::MAX in test_binary_search, we workaround this by setting the size to
|
||||
// zero for ZSTs.
|
||||
len = 0;
|
||||
}
|
||||
if let Some(struct_type) = ty.is_struct()
|
||||
&& struct_type.get_field_count() == 0
|
||||
{
|
||||
// NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a
|
||||
// size of usize::MAX in test_binary_search, we workaround this by setting the size to
|
||||
// zero for ZSTs.
|
||||
len = 0;
|
||||
}
|
||||
|
||||
self.context.new_array_type(None, ty, len)
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
|||
let ty = match *self.ty.kind() {
|
||||
// NOTE: we cannot remove this match like in the LLVM codegen because the call
|
||||
// to fn_ptr_backend_type handle the on-stack attribute.
|
||||
// TODO(antoyo): find a less hackish way to hande the on-stack attribute.
|
||||
// TODO(antoyo): find a less hackish way to handle the on-stack attribute.
|
||||
ty::FnPtr(sig_tys, hdr) => cx
|
||||
.fn_ptr_backend_type(cx.fn_abi_of_fn_ptr(sig_tys.with(hdr), ty::List::empty())),
|
||||
_ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO),
|
||||
|
|
|
|||
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