Merge from rust-lang/rust

This commit is contained in:
Laurențiu Nicola 2025-06-23 12:17:31 +03:00
commit 3c4a690d01
888 changed files with 34234 additions and 19450 deletions

54
.github/ISSUE_TEMPLATE/rustdoc.md vendored Normal file
View 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 -->

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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))
}
}

View file

@ -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() {

View file

@ -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(), &note)?);
}
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(), &note)?);
}
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 {

View file

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

View file

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

View file

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

View 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;
}
},
})
}
}

View file

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

View 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))
}
}

View file

@ -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, &param, &mut feature)?,
Some(sym::since) => insert_value_into_option_or_error(cx, &param, &mut since)?,
let word = param.path().word();
match word.map(|i| i.name) {
Some(sym::feature) => {
insert_value_into_option_or_error(cx, &param, &mut feature, word.unwrap())?
}
Some(sym::since) => {
insert_value_into_option_or_error(cx, &param, &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, &param, &mut feature)?,
Some(sym::reason) => insert_value_into_option_or_error(cx, &param, &mut reason)?,
let word = param.path().word();
match word.map(|i| i.name) {
Some(sym::feature) => {
insert_value_into_option_or_error(cx, &param, &mut feature, word.unwrap())?
}
Some(sym::reason) => {
insert_value_into_option_or_error(cx, &param, &mut reason, word.unwrap())?
}
Some(sym::issue) => {
insert_value_into_option_or_error(cx, &param, &mut issue)?;
insert_value_into_option_or_error(cx, &param, &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, &param, &mut implied_by)?
insert_value_into_option_or_error(cx, &param, &mut implied_by, word.unwrap())?
}
Some(sym::old_name) => {
insert_value_into_option_or_error(cx, &param, &mut old_name, word.unwrap())?
}
Some(sym::old_name) => insert_value_into_option_or_error(cx, &param, &mut old_name)?,
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param.span(),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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![]);

View file

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

View file

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

View file

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

View file

@ -606,6 +606,7 @@ fn make_format_args(
template,
arguments: args,
uncooked_fmt_str,
is_source_literal,
}))
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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![],

View 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*"
]
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -19,4 +19,5 @@ tools/llvmint-2
llvm
build_system/target
config.toml
build
build
rustlantis

View file

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

View file

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

View file

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

View 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"]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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)
}

View 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");
}

View 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(())
}

View file

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

View file

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

View file

@ -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(&current_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)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2025-05-12"
channel = "nightly-2025-05-21"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]

View file

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

View file

@ -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!(),
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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() {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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))
}
_ => {

View file

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

View file

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

View file

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