Merge pull request #20960 from rust-lang/rustc-pull

minor: Rustc pull update
This commit is contained in:
Laurențiu Nicola 2025-11-04 08:18:22 +00:00 committed by GitHub
commit 5873327cd4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3648 changed files with 55330 additions and 26590 deletions

View file

@ -50,7 +50,7 @@ for larger features an implementation could be broken up into multiple PRs.
[stabilization-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#stabilization-pr
[doc-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#documentation-prs
[nightly-style-procedure]: https://github.com/rust-lang/style-team/blob/main/nightly-style-procedure.md
[Style Guide]: https://github.com/rust-lang/rust/tree/master/src/doc/style-guide
[Style Guide]: https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide
### Unresolved Questions
<!--

View file

@ -323,7 +323,7 @@ jobs:
# If a some dependent job has failed, this exits with 1.
- name: calculate the correct exit status
run: jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< '${{ toJson(needs) }}'
# Publish the toolstate if an auto build succeeds (just before push to master)
# Publish the toolstate if an auto build succeeds (just before push to the default branch)
- name: publish toolstate
run: src/ci/publish_toolstate.sh
shell: bash

View file

@ -1,4 +1,4 @@
# Workflow that runs after a merge to master, analyses changes in test executions
# Workflow that runs after a merge to the default branch, analyses changes in test executions
# and posts the result to the merged PR.
name: Post merge analysis

4
.gitignore vendored
View file

@ -90,6 +90,10 @@ node_modules
## Rustdoc GUI tests
tests/rustdoc-gui/src/**.lock
## Test dashboard
.citool-cache/
test-dashboard/
## direnv
/.envrc
/.direnv/

View file

@ -427,6 +427,7 @@ Marcell Pardavi <marcell.pardavi@gmail.com>
Marco Ieni <11428655+MarcoIeni@users.noreply.github.com>
Marcus Klaas de Vries <mail@marcusklaas.nl>
Margaret Meyerhofer <mmeyerho@andrew.cmu.edu> <mmeyerho@andrew>
Marijn Schouten <mhkbst@gmail.com> <hkBst@users.noreply.github.com>
Mark Mansi <markm@cs.wisc.edu>
Mark Mansi <markm@cs.wisc.edu> <m.mim95@gmail.com>
Mark Rousskov <mark.simulacrum@gmail.com>
@ -679,6 +680,7 @@ Valerii Lashmanov <vflashm@gmail.com>
Vitali Haravy <HumaneProgrammer@gmail.com> Vitali Haravy <humaneprogrammer@gmail.com>
Vitaly Shukela <vi0oss@gmail.com>
Waffle Lapkin <waffle.lapkin@gmail.com>
Waffle Lapkin <waffle.lapkin@gmail.com> <Waffle Lapkin>
Waffle Lapkin <waffle.lapkin@gmail.com> <waffle.lapkin@tasking.com>
Weihang Lo <me@weihanglo.tw>
Weihang Lo <me@weihanglo.tw> <weihanglo@users.noreply.github.com>

View file

@ -75,7 +75,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4"
dependencies = [
"anstyle",
"unicode-width 0.2.1",
"unicode-width 0.2.2",
]
[[package]]
name = "annotate-snippets"
version = "0.12.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47224528f74de27d1d06aad6a5dda4f865b6ebe2e56c538943d746a7270cb67e"
dependencies = [
"anstyle",
"unicode-width 0.2.2",
]
[[package]]
@ -95,9 +105,9 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.11"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-lossy"
@ -136,7 +146,7 @@ dependencies = [
"anstyle-lossy",
"anstyle-parse",
"html-escape",
"unicode-width 0.2.1",
"unicode-width 0.2.2",
]
[[package]]
@ -570,7 +580,7 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
[[package]]
name = "clippy"
version = "0.1.92"
version = "0.1.93"
dependencies = [
"anstream",
"askama",
@ -597,7 +607,7 @@ dependencies = [
[[package]]
name = "clippy_config"
version = "0.1.92"
version = "0.1.93"
dependencies = [
"clippy_utils",
"itertools",
@ -620,7 +630,7 @@ dependencies = [
[[package]]
name = "clippy_lints"
version = "0.1.92"
version = "0.1.93"
dependencies = [
"arrayvec",
"cargo_metadata 0.18.1",
@ -645,13 +655,14 @@ version = "0.0.1"
dependencies = [
"clippy_config",
"clippy_utils",
"itertools",
"regex",
"rustc-semver",
]
[[package]]
name = "clippy_utils"
version = "0.1.92"
version = "0.1.93"
dependencies = [
"arrayvec",
"itertools",
@ -676,7 +687,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
dependencies = [
"serde",
"termcolor",
"unicode-width 0.2.1",
"unicode-width 0.2.2",
]
[[package]]
@ -807,7 +818,7 @@ dependencies = [
"encode_unicode",
"libc",
"once_cell",
"unicode-width 0.2.1",
"unicode-width 0.2.2",
"windows-sys 0.59.0",
]
@ -1055,7 +1066,7 @@ dependencies = [
[[package]]
name = "declare_clippy_lint"
version = "0.1.92"
version = "0.1.93"
[[package]]
name = "derive-where"
@ -1164,7 +1175,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users 0.5.2",
"windows-sys 0.60.2",
"windows-sys 0.61.2",
]
[[package]]
@ -1277,7 +1288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
"windows-sys 0.60.2",
"windows-sys 0.52.0",
]
[[package]]
@ -1484,7 +1495,7 @@ version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
dependencies = [
"unicode-width 0.2.1",
"unicode-width 0.2.2",
]
[[package]]
@ -1886,7 +1897,7 @@ dependencies = [
"console",
"number_prefix",
"portable-atomic",
"unicode-width 0.2.1",
"unicode-width 0.2.2",
"web-time",
]
@ -2090,9 +2101,9 @@ checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7"
[[package]]
name = "libc"
version = "0.2.174"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "libdbus-sys"
@ -2106,9 +2117,9 @@ dependencies = [
[[package]]
name = "libffi"
version = "4.1.1"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7681c6fab541f799a829e44a445a0666cf8d8a6cfebf89419e6aed52c604e87"
checksum = "0444124f3ffd67e1b0b0c661a7f81a278a135eb54aaad4078e79fbc8be50c8a5"
dependencies = [
"libc",
"libffi-sys",
@ -2116,9 +2127,9 @@ dependencies = [
[[package]]
name = "libffi-sys"
version = "3.3.2"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b0d828d367b4450ed08e7d510dc46636cd660055f50d67ac943bfe788767c29"
checksum = "3d722da8817ea580d0669da6babe2262d7b86a1af1103da24102b8bb9c101ce7"
dependencies = [
"cc",
]
@ -2143,7 +2154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [
"cfg-if",
"windows-targets 0.53.3",
"windows-targets 0.52.6",
]
[[package]]
@ -2205,9 +2216,9 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "litemap"
@ -2374,7 +2385,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08"
dependencies = [
"windows-sys 0.60.2",
"windows-sys 0.61.2",
]
[[package]]
@ -3755,7 +3766,9 @@ dependencies = [
name = "rustc_errors"
version = "0.0.0"
dependencies = [
"annotate-snippets 0.11.5",
"annotate-snippets 0.12.7",
"anstream",
"anstyle",
"derive_setters",
"rustc_abi",
"rustc_ast",
@ -3772,7 +3785,6 @@ dependencies = [
"rustc_span",
"serde",
"serde_json",
"termcolor",
"termize",
"tracing",
"windows 0.61.3",
@ -4119,6 +4131,7 @@ name = "rustc_log"
version = "0.0.0"
dependencies = [
"tracing",
"tracing-core",
"tracing-subscriber",
"tracing-tree",
]
@ -4325,11 +4338,10 @@ dependencies = [
"rustc_macros",
"rustc_session",
"rustc_span",
"termcolor",
"thin-vec",
"tracing",
"unicode-normalization",
"unicode-width 0.2.1",
"unicode-width 0.2.2",
]
[[package]]
@ -4588,7 +4600,7 @@ dependencies = [
"sha1",
"sha2",
"tracing",
"unicode-width 0.2.1",
"unicode-width 0.2.2",
]
[[package]]
@ -4885,15 +4897,15 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.0.8"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.60.2",
"windows-sys 0.52.0",
]
[[package]]
@ -5261,9 +5273,9 @@ dependencies = [
[[package]]
name = "stringdex"
version = "0.0.1-alpha10"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa846a7d509d1828a4f90962dc09810e161abcada7fc6a921e92c168d0811d7"
checksum = "18b3bd4f10d15ef859c40291769f0d85209de6b0f1c30713ff9cdf45ac43ea36"
dependencies = [
"stacker",
]
@ -5934,9 +5946,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-width"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "unicode-xid"
@ -6221,7 +6233,7 @@ dependencies = [
"bumpalo",
"leb128fmt",
"memchr",
"unicode-width 0.2.1",
"unicode-width 0.2.2",
"wasm-encoder 0.240.0",
]

View file

@ -1,3 +1,214 @@
Version 1.91.0 (2025-10-30)
==========================
<a id="1.91.0-Language"></a>
Language
--------
- [Lower pattern bindings in the order they're written and base drop order on primary bindings' order](https://github.com/rust-lang/rust/pull/143764)
- [Stabilize declaration of C-style variadic functions for `sysv64`, `win64`, `efiapi`, and `aapcs` ABIs](https://github.com/rust-lang/rust/pull/144066).
This brings these ABIs in line with the C ABI: variadic functions can be declared in extern blocks but not defined.
- [Add `dangling_pointers_from_locals` lint to warn against dangling pointers from local variables](https://github.com/rust-lang/rust/pull/144322)
- [Upgrade `semicolon_in_expressions_from_macros` from warn to deny](https://github.com/rust-lang/rust/pull/144369)
- [Stabilize LoongArch32 inline assembly](https://github.com/rust-lang/rust/pull/144402)
- [Add warn-by-default `integer_to_ptr_transmutes` lint against integer-to-pointer transmutes](https://github.com/rust-lang/rust/pull/144531)
- [Stabilize `sse4a` and `tbm` target features](https://github.com/rust-lang/rust/pull/144542)
- [Add `target_env = "macabi"` and `target_env = "sim"` cfgs](https://github.com/rust-lang/rust/pull/139451) as replacements for the `target_abi` cfgs with the same values.
<a id="1.91.0-Compiler"></a>
Compiler
--------
- [Don't warn on never-to-any `as` casts as unreachable](https://github.com/rust-lang/rust/pull/144804)
<a id="1.91.0-Platform-Support"></a>
Platform Support
----------------
- [Promote `aarch64-pc-windows-gnullvm` and `x86_64-pc-windows-gnullvm` to Tier 2 with host tools.](https://github.com/rust-lang/rust/pull/143031)
Note: llvm-tools and MSI installers are missing but will be added in future releases.
- [Promote `aarch64-pc-windows-msvc` to Tier 1](https://github.com/rust-lang/rust/pull/145682)
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.91.0-Libraries"></a>
Libraries
---------
- [Print thread ID in panic message](https://github.com/rust-lang/rust/pull/115746)
- [Fix overly restrictive lifetime in `core::panic::Location::file` return type](https://github.com/rust-lang/rust/pull/132087)
- [Guarantee parameter order for `_by()` variants of `min` / `max`/ `minmax` in `std::cmp`](https://github.com/rust-lang/rust/pull/139357)
- [Document assumptions about `Clone` and `Eq` traits](https://github.com/rust-lang/rust/pull/144330/)
- [`std::thread`: Return error if setting thread stack size fails](https://github.com/rust-lang/rust/pull/144210)
This used to panic within the standard library.
<a id="1.91.0-Stabilized-APIs"></a>
Stabilized APIs
---------------
- [`Path::file_prefix`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#method.file_prefix)
- [`AtomicPtr::fetch_ptr_add`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_ptr_add)
- [`AtomicPtr::fetch_ptr_sub`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_ptr_sub)
- [`AtomicPtr::fetch_byte_add`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_byte_add)
- [`AtomicPtr::fetch_byte_sub`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_byte_sub)
- [`AtomicPtr::fetch_or`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_or)
- [`AtomicPtr::fetch_and`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_and)
- [`AtomicPtr::fetch_xor`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_xor)
- [`{integer}::strict_add`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_add)
- [`{integer}::strict_sub`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_sub)
- [`{integer}::strict_mul`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_mul)
- [`{integer}::strict_div`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_div)
- [`{integer}::strict_div_euclid`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_div_euclid)
- [`{integer}::strict_rem`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_rem)
- [`{integer}::strict_rem_euclid`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_rem_euclid)
- [`{integer}::strict_neg`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_neg)
- [`{integer}::strict_shl`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shl)
- [`{integer}::strict_shr`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shr)
- [`{integer}::strict_pow`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_pow)
- [`i{N}::strict_add_unsigned`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_add_unsigned)
- [`i{N}::strict_sub_unsigned`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_sub_unsigned)
- [`i{N}::strict_abs`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_abs)
- [`u{N}::strict_add_signed`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_add_signed)
- [`u{N}::strict_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_sub_signed)
- [`PanicHookInfo::payload_as_str`](https://doc.rust-lang.org/stable/std/panic/struct.PanicHookInfo.html#method.payload_as_str)
- [`core::iter::chain`](https://doc.rust-lang.org/stable/core/iter/fn.chain.html)
- [`u{N}::checked_signed_diff`](https://doc.rust-lang.org/stable/std/primitive.u16.html#method.checked_signed_diff)
- [`core::array::repeat`](https://doc.rust-lang.org/stable/core/array/fn.repeat.html)
- [`PathBuf::add_extension`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.add_extension)
- [`PathBuf::with_added_extension`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.with_added_extension)
- [`Duration::from_mins`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_mins)
- [`Duration::from_hours`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_hours)
- [`impl PartialEq<str> for PathBuf`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#impl-PartialEq%3Cstr%3E-for-PathBuf)
- [`impl PartialEq<String> for PathBuf`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#impl-PartialEq%3CString%3E-for-PathBuf)
- [`impl PartialEq<str> for Path`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#impl-PartialEq%3Cstr%3E-for-Path)
- [`impl PartialEq<String> for Path`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#impl-PartialEq%3CString%3E-for-Path)
- [`impl PartialEq<PathBuf> for String`](https://doc.rust-lang.org/stable/std/string/struct.String.html#impl-PartialEq%3CPathBuf%3E-for-String)
- [`impl PartialEq<Path> for String`](https://doc.rust-lang.org/stable/std/string/struct.String.html#impl-PartialEq%3CPath%3E-for-String)
- [`impl PartialEq<PathBuf> for str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-PartialEq%3CPathBuf%3E-for-str)
- [`impl PartialEq<Path> for str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-PartialEq%3CPath%3E-for-str)
- [`Ipv4Addr::from_octets`](https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.from_octets)
- [`Ipv6Addr::from_octets`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.from_octets)
- [`Ipv6Addr::from_segments`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.from_segments)
- [`impl<T> Default for Pin<Box<T>> where Box<T>: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CBox%3CT%3E%3E)
- [`impl<T> Default for Pin<Rc<T>> where Rc<T>: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CRc%3CT%3E%3E)
- [`impl<T> Default for Pin<Arc<T>> where Arc<T>: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CArc%3CT%3E%3E)
- [`Cell::as_array_of_cells`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#method.as_array_of_cells)
- [`u{N}::carrying_add`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_add)
- [`u{N}::borrowing_sub`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.borrowing_sub)
- [`u{N}::carrying_mul`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_mul)
- [`u{N}::carrying_mul_add`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_mul_add)
- [`BTreeMap::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html#method.extract_if)
- [`BTreeSet::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeSet.html#method.extract_if)
- [`impl Debug for windows::ffi::EncodeWide<'_>`](https://doc.rust-lang.org/stable/std/os/windows/ffi/struct.EncodeWide.html#impl-Debug-for-EncodeWide%3C'_%3E)
- [`str::ceil_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.ceil_char_boundary)
- [`str::floor_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.floor_char_boundary)
- [`impl Sum for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Sum-for-Saturating%3Cu32%3E)
- [`impl Sum<&Self> for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Sum%3C%26Saturating%3Cu32%3E%3E-for-Saturating%3Cu32%3E)
- [`impl Product for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Product-for-Saturating%3Cu32%3E)
- [`impl Product<&Self> for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Product%3C%26Saturating%3Cu32%3E%3E-for-Saturating%3Cu32%3E)
These previously stable APIs are now stable in const contexts:
- [`<[T; N]>::each_ref`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_ref)
- [`<[T; N]>::each_mut`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_mut)
- [`OsString::new`](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.new)
- [`PathBuf::new`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.new)
- [`TypeId::of`](https://doc.rust-lang.org/stable/std/any/struct.TypeId.html#method.of)
- [`ptr::with_exposed_provenance`](https://doc.rust-lang.org/stable/std/ptr/fn.with_exposed_provenance.html)
- [`ptr::with_exposed_provenance_mut`](https://doc.rust-lang.org/stable/std/ptr/fn.with_exposed_provenance_mut.html)
<a id="1.91.0-Cargo"></a>
Cargo
-----
- 🎉 Stabilize `build.build-dir`.
This config sets the directory where intermediate build artifacts are stored.
These artifacts are produced by Cargo and rustc during the build process.
End users usually won't need to interact with them, and the layout inside
`build-dir` is an implementation detail that may change without notice.
([config doc](https://doc.rust-lang.org/stable/cargo/reference/config.html#buildbuild-dir))
([build cache doc](https://doc.rust-lang.org/stable/cargo/reference/build-cache.html))
[#15833](https://github.com/rust-lang/cargo/pull/15833)
[#15840](https://github.com/rust-lang/cargo/pull/15840)
- The `--target` flag and the `build.target` configuration can now take literal
`"host-tuple"` string, which will internally be substituted by the host
machine's target triple.
[#15838](https://github.com/rust-lang/cargo/pull/15838)
[#16003](https://github.com/rust-lang/cargo/pull/16003)
[#16032](https://github.com/rust-lang/cargo/pull/16032)
<a id="1.91.0-Rustdoc"></a>
Rustdoc
-----
- [In search results, rank doc aliases lower than non-alias items with the same name](https://github.com/rust-lang/rust/pull/145100)
- [Raw pointers now work in type-based search like references](https://github.com/rust-lang/rust/pull/145731). This means you can now search for things like `*const u8 ->`, and additionally functions that take or return raw pointers will now display their signature properly in search results.
<a id="1.91.0-Compatibility-Notes"></a>
Compatibility Notes
-------------------
- [Always require coroutine captures to be drop-live](https://github.com/rust-lang/rust/pull/144156)
- [Apple: Always pass SDK root when linking with `cc`, and pass it via `SDKROOT` env var](https://github.com/rust-lang/rust/pull/131477). This should fix linking issues with `rustc` running inside Xcode. Libraries in `/usr/local/lib` may no longer be linked automatically, if you develop or use a crate that relies on this, you should explicitly set `cargo::rustc-link-search=/usr/local/lib` in a `build.rs` script.
- [Relaxed bounds in associated type bound position like in `TraitRef<AssocTy: ?Sized>` are now correctly forbidden](https://github.com/rust-lang/rust/pull/135331)
- [Add unstable `#[sanitize(xyz = "on|off")]` built-in attribute that shadows procedural macros with the same name](https://github.com/rust-lang/rust/pull/142681)
- [Fix the drop checker being more permissive for bindings declared with let-else](https://github.com/rust-lang/rust/pull/143028)
- [Be more strict when parsing attributes, erroring on many invalid attributes](https://github.com/rust-lang/rust/pull/144689)
- [Error on invalid `#[should_panic]` attributes](https://github.com/rust-lang/rust/pull/143808)
- [Error on invalid `#[link]` attributes](https://github.com/rust-lang/rust/pull/143193)
- [Mark all deprecation lints in name resolution as deny-by-default and also report in dependencies](https://github.com/rust-lang/rust/pull/143929)
- The lint `semicolon_in_expressions_from_macros`, for `macro_rules!` macros in expression position that expand to end in a semicolon (`;`), is now deny-by-default. It was already warn-by-default, and a future compatibility warning (FCW) that warned even in dependencies. This lint will become a hard error in the future.
- [Trait impl modifiers (e.g., `unsafe`, `!`, `default`) in inherent impls are no longer syntactically valid](https://github.com/rust-lang/rust/pull/144386)
- [Start reporting future breakage for `ill_formed_attribute_input` in dependencies](https://github.com/rust-lang/rust/pull/144544)
- [Restrict the scope of temporaries created by the macros `pin!`, `format_args!`, `write!`, and `writeln!` in `if let` scrutinees in Rust Edition 2024.](https://github.com/rust-lang/rust/pull/145342) This applies [Rust Edition 2024's `if let` temporary scope rules](https://doc.rust-lang.org/edition-guide/rust-2024/temporary-if-let-scope.html) to these temporaries, which previously could live past the `if` expression regardless of Edition.
- [Invalid numeric literal suffixes in tuple indexing, tuple struct indexing, and struct field name positions are now correctly rejected](https://github.com/rust-lang/rust/pull/145463)
- [Closures marked with the keyword `static` are now syntactically invalid](https://github.com/rust-lang/rust/pull/145604)
- [Shebangs inside `--cfg` and `--check-cfg` arguments are no longer allowed](https://github.com/rust-lang/rust/pull/146211)
- [Add future incompatibility lint for temporary lifetime shortening in Rust 1.92](https://github.com/rust-lang/rust/pull/147056)
Cargo compatibility notes:
- `cargo publish` no longer keeps `.crate` tarballs as final build artifacts
when `build.build-dir` is set. These tarballs were previously included due to
an oversight and are now treated as intermediate artifacts.
To get `.crate` tarballs as final artifacts, use `cargo package`.
In a future version, this change will apply regardless of `build.build-dir`.
[#15910](https://github.com/rust-lang/cargo/pull/15910)
- Adjust Cargo messages to match rustc diagnostic style.
This changes some of the terminal colors used by Cargo messages.
[#15928](https://github.com/rust-lang/cargo/pull/15928)
- Tools and projects relying on the
[internal details of Cargo's `build-dir`](https://doc.rust-lang.org/cargo/reference/build-cache.html)
may not work for users changing their `build-dir` layout.
For those doing so, we'd recommend proactively testing these cases
particularly as we are considering changing the default location of the `build-dir` in the future
([cargo#16147](https://github.com/rust-lang/cargo/issues/16147)).
If you can't migrate off of Cargo's internal details,
we'd like to learn more about your use case as we prepare to change the layout of the `build-dir`
([cargo#15010](https://github.com/rust-lang/cargo/issues/15010)).
<a id="1.91.0-Internal-Changes"></a>
Internal Changes
----------------
These changes do not affect any public interfaces of Rust, but they represent
significant improvements to the performance or internals of rustc and related
tools.
- [Update to LLVM 21](https://github.com/rust-lang/rust/pull/143684)
Version 1.90.0 (2025-09-18)
===========================

View file

@ -789,14 +789,14 @@ pub struct PatField {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
pub enum ByRef {
Yes(Mutability),
Yes(Pinnedness, Mutability),
No,
}
impl ByRef {
#[must_use]
pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
if let ByRef::Yes(old_mutbl) = &mut self {
if let ByRef::Yes(_, old_mutbl) = &mut self {
*old_mutbl = cmp::min(*old_mutbl, mutbl);
}
self
@ -814,20 +814,33 @@ pub struct BindingMode(pub ByRef, pub Mutability);
impl BindingMode {
pub const NONE: Self = Self(ByRef::No, Mutability::Not);
pub const REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Not);
pub const REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Not);
pub const REF_PIN: Self =
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Not);
pub const MUT: Self = Self(ByRef::No, Mutability::Mut);
pub const REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Not);
pub const MUT_REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Mut);
pub const MUT_REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Mut);
pub const REF_MUT: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Not);
pub const REF_PIN_MUT: Self =
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Not);
pub const MUT_REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Mut);
pub const MUT_REF_PIN: Self =
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Mut);
pub const MUT_REF_MUT: Self =
Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Mut);
pub const MUT_REF_PIN_MUT: Self =
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Mut);
pub fn prefix_str(self) -> &'static str {
match self {
Self::NONE => "",
Self::REF => "ref ",
Self::REF_PIN => "ref pin const ",
Self::MUT => "mut ",
Self::REF_MUT => "ref mut ",
Self::REF_PIN_MUT => "ref pin mut ",
Self::MUT_REF => "mut ref ",
Self::MUT_REF_PIN => "mut ref pin ",
Self::MUT_REF_MUT => "mut ref mut ",
Self::MUT_REF_PIN_MUT => "mut ref pin mut ",
}
}
}
@ -869,11 +882,11 @@ pub enum PatKind {
Struct(Option<Box<QSelf>>, Path, ThinVec<PatField>, PatFieldsRest),
/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
TupleStruct(Option<Box<QSelf>>, Path, ThinVec<Box<Pat>>),
TupleStruct(Option<Box<QSelf>>, Path, ThinVec<Pat>),
/// An or-pattern `A | B | C`.
/// Invariant: `pats.len() >= 2`.
Or(ThinVec<Box<Pat>>),
Or(ThinVec<Pat>),
/// A possibly qualified path pattern.
/// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants
@ -882,7 +895,7 @@ pub enum PatKind {
Path(Option<Box<QSelf>>, Path),
/// A tuple pattern (`(a, b)`).
Tuple(ThinVec<Box<Pat>>),
Tuple(ThinVec<Pat>),
/// A `box` pattern.
Box(Box<Pat>),
@ -900,7 +913,7 @@ pub enum PatKind {
Range(Option<Box<Expr>>, Option<Box<Expr>>, Spanned<RangeEnd>),
/// A slice pattern `[a, b, c]`.
Slice(ThinVec<Box<Pat>>),
Slice(ThinVec<Pat>),
/// A rest pattern `..`.
///
@ -2579,7 +2592,10 @@ pub enum TyPatKind {
/// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
Range(Option<Box<AnonConst>>, Option<Box<AnonConst>>, Spanned<RangeEnd>),
Or(ThinVec<Box<TyPat>>),
/// A `!null` pattern for raw pointers.
NotNull,
Or(ThinVec<TyPat>),
/// Placeholder for a pattern that wasn't syntactically well formed in some way.
Err(ErrorGuaranteed),
@ -3537,8 +3553,9 @@ impl Item {
ItemKind::Const(i) => Some(&i.generics),
ItemKind::Fn(i) => Some(&i.generics),
ItemKind::TyAlias(i) => Some(&i.generics),
ItemKind::TraitAlias(_, generics, _)
| ItemKind::Enum(_, generics, _)
ItemKind::TraitAlias(i) => Some(&i.generics),
ItemKind::Enum(_, generics, _)
| ItemKind::Struct(_, generics, _)
| ItemKind::Union(_, generics, _) => Some(&generics),
ItemKind::Trait(i) => Some(&i.generics),
@ -3620,6 +3637,15 @@ impl Default for FnHeader {
}
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct TraitAlias {
pub constness: Const,
pub ident: Ident,
pub generics: Generics,
#[visitable(extra = BoundKind::Bound)]
pub bounds: GenericBounds,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct Trait {
pub constness: Const,
@ -3633,49 +3659,26 @@ pub struct Trait {
pub items: ThinVec<Box<AssocItem>>,
}
/// The location of a where clause on a `TyAlias` (`Span`) and whether there was
/// a `where` keyword (`bool`). This is split out from `WhereClause`, since there
/// are two locations for where clause on type aliases, but their predicates
/// are concatenated together.
///
/// Take this example:
/// ```ignore (only-for-syntax-highlight)
/// trait Foo {
/// type Assoc<'a, 'b> where Self: 'a, Self: 'b;
/// }
/// impl Foo for () {
/// type Assoc<'a, 'b> where Self: 'a = () where Self: 'b;
/// // ^^^^^^^^^^^^^^ first where clause
/// // ^^^^^^^^^^^^^^ second where clause
/// }
/// ```
///
/// If there is no where clause, then this is `false` with `DUMMY_SP`.
#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
pub struct TyAliasWhereClause {
pub has_where_token: bool,
pub span: Span,
}
/// The span information for the two where clauses on a `TyAlias`.
#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
pub struct TyAliasWhereClauses {
/// Before the equals sign.
pub before: TyAliasWhereClause,
/// After the equals sign.
pub after: TyAliasWhereClause,
/// The index in `TyAlias.generics.where_clause.predicates` that would split
/// into predicates from the where clause before the equals sign and the ones
/// from the where clause after the equals sign.
pub split: usize,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct TyAlias {
pub defaultness: Defaultness,
pub ident: Ident,
pub generics: Generics,
pub where_clauses: TyAliasWhereClauses,
/// There are two locations for where clause on type aliases. This represents the second
/// where clause, before the semicolon. The first where clause is stored inside `generics`.
///
/// Take this example:
/// ```ignore (only-for-syntax-highlight)
/// trait Foo {
/// type Assoc<'a, 'b> where Self: 'a, Self: 'b;
/// }
/// impl Foo for () {
/// type Assoc<'a, 'b> where Self: 'a = () where Self: 'b;
/// // ^^^^^^^^^^^^^^ before where clause
/// // ^^^^^^^^^^^^^^ after where clause
/// }
/// ```
pub after_where_clause: WhereClause,
#[visitable(extra = BoundKind::Bound)]
pub bounds: GenericBounds,
pub ty: Option<Box<Ty>>,
@ -3700,6 +3703,9 @@ pub struct TraitImplHeader {
#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
pub struct FnContract {
/// Declarations of variables accessible both in the `requires` and
/// `ensures` clauses.
pub declarations: ThinVec<Stmt>,
pub requires: Option<Box<Expr>>,
pub ensures: Option<Box<Expr>>,
}
@ -3815,7 +3821,7 @@ pub enum ItemKind {
/// Trait alias.
///
/// E.g., `trait Foo = Bar + Quux;`.
TraitAlias(Ident, Generics, GenericBounds),
TraitAlias(Box<TraitAlias>),
/// An implementation.
///
/// E.g., `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`.
@ -3848,7 +3854,7 @@ impl ItemKind {
| ItemKind::Struct(ident, ..)
| ItemKind::Union(ident, ..)
| ItemKind::Trait(box Trait { ident, .. })
| ItemKind::TraitAlias(ident, ..)
| ItemKind::TraitAlias(box TraitAlias { ident, .. })
| ItemKind::MacroDef(ident, _)
| ItemKind::Delegation(box Delegation { ident, .. }) => Some(ident),
@ -3905,7 +3911,7 @@ impl ItemKind {
| Self::Struct(_, generics, _)
| Self::Union(_, generics, _)
| Self::Trait(box Trait { generics, .. })
| Self::TraitAlias(_, generics, _)
| Self::TraitAlias(box TraitAlias { generics, .. })
| Self::Impl(Impl { generics, .. }) => Some(generics),
_ => None,
}
@ -4067,8 +4073,8 @@ mod size_asserts {
static_assert_size!(GenericBound, 88);
static_assert_size!(Generics, 40);
static_assert_size!(Impl, 64);
static_assert_size!(Item, 144);
static_assert_size!(ItemKind, 80);
static_assert_size!(Item, 136);
static_assert_size!(ItemKind, 72);
static_assert_size!(LitKind, 24);
static_assert_size!(Local, 96);
static_assert_size!(MetaItemLit, 40);

View file

@ -86,10 +86,10 @@ impl AttributeExt for Attribute {
/// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
/// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
/// a doc comment) will return `false`.
fn is_doc_comment(&self) -> bool {
fn is_doc_comment(&self) -> Option<Span> {
match self.kind {
AttrKind::Normal(..) => false,
AttrKind::DocComment(..) => true,
AttrKind::Normal(..) => None,
AttrKind::DocComment(..) => Some(self.span),
}
}
@ -776,7 +776,7 @@ pub trait AttributeExt: Debug {
/// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
/// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
/// a doc comment) will return `false`.
fn is_doc_comment(&self) -> bool;
fn is_doc_comment(&self) -> Option<Span>;
#[inline]
fn has_name(&self, name: Symbol) -> bool {
@ -863,8 +863,9 @@ impl Attribute {
AttributeExt::path_matches(self, name)
}
// on ast attributes we return a bool since that's what most code already expects
pub fn is_doc_comment(&self) -> bool {
AttributeExt::is_doc_comment(self)
AttributeExt::is_doc_comment(self).is_some()
}
#[inline]

View file

@ -1,7 +1,5 @@
use rustc_span::{Symbol, sym};
use crate::attr::{self, AttributeExt};
#[derive(Debug)]
pub enum EntryPointType {
/// This function is not an entrypoint.
@ -30,11 +28,11 @@ pub enum EntryPointType {
}
pub fn entry_point_type(
attrs: &[impl AttributeExt],
has_rustc_main: bool,
at_root: bool,
name: Option<Symbol>,
) -> EntryPointType {
if attr::contains_name(attrs, sym::rustc_main) {
if has_rustc_main {
EntryPointType::RustcMainAttr
} else if let Some(name) = name
&& name == sym::main

View file

@ -3,7 +3,9 @@ use rustc_span::{Symbol, sym};
#[derive(Clone, Debug, Copy, Eq, PartialEq, HashStable_Generic)]
pub enum AllocatorKind {
/// Use `#[global_allocator]` as global allocator.
Global,
/// Use the default implementation in libstd as global allocator.
Default,
}
@ -15,25 +17,37 @@ pub fn default_fn_name(base: Symbol) -> String {
format!("__rdl_{base}")
}
pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'static str {
match alloc_error_handler_kind {
AllocatorKind::Global => "__rg_oom",
AllocatorKind::Default => "__rdl_oom",
}
}
pub const ALLOC_ERROR_HANDLER: Symbol = sym::alloc_error_handler;
pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2";
/// Argument or return type for methods in the allocator shim
#[derive(Copy, Clone)]
pub enum AllocatorTy {
Layout,
Never,
Ptr,
ResultPtr,
Unit,
Usize,
}
/// Some allocator methods are known to the compiler: they act more like
/// intrinsics/language primitives than library-defined functions.
/// FIXME: ideally this would be derived from attributes like `#[rustc_allocator]`,
/// so we don't have two sources of truth.
#[derive(Copy, Clone, Debug)]
pub enum SpecialAllocatorMethod {
Alloc,
AllocZeroed,
Dealloc,
Realloc,
}
/// A method that will be codegened in the allocator shim.
#[derive(Copy, Clone)]
pub struct AllocatorMethod {
pub name: Symbol,
pub special: Option<SpecialAllocatorMethod>,
pub inputs: &'static [AllocatorMethodInput],
pub output: AllocatorTy,
}
@ -46,11 +60,13 @@ pub struct AllocatorMethodInput {
pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
AllocatorMethod {
name: sym::alloc,
special: Some(SpecialAllocatorMethod::Alloc),
inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }],
output: AllocatorTy::ResultPtr,
},
AllocatorMethod {
name: sym::dealloc,
special: Some(SpecialAllocatorMethod::Dealloc),
inputs: &[
AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr },
AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout },
@ -59,6 +75,7 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
},
AllocatorMethod {
name: sym::realloc,
special: Some(SpecialAllocatorMethod::Realloc),
inputs: &[
AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr },
AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout },
@ -68,6 +85,7 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
},
AllocatorMethod {
name: sym::alloc_zeroed,
special: Some(SpecialAllocatorMethod::AllocZeroed),
inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }],
output: AllocatorTy::ResultPtr,
},

View file

@ -368,6 +368,7 @@ macro_rules! common_visitor_and_walkers {
crate::tokenstream::TokenStream,
Movability,
Mutability,
Pinnedness,
Result<(), rustc_span::ErrorGuaranteed>,
rustc_data_structures::fx::FxHashMap<Symbol, usize>,
rustc_span::ErrorGuaranteed,
@ -389,9 +390,9 @@ macro_rules! common_visitor_and_walkers {
ThinVec<(NodeId, Path)>,
ThinVec<PathSegment>,
ThinVec<PreciseCapturingArg>,
ThinVec<Box<Pat>>,
ThinVec<Pat>,
ThinVec<Box<Ty>>,
ThinVec<Box<TyPat>>,
ThinVec<TyPat>,
);
// This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
@ -471,8 +472,6 @@ macro_rules! common_visitor_and_walkers {
TraitBoundModifiers,
TraitObjectSyntax,
TyAlias,
TyAliasWhereClause,
TyAliasWhereClauses,
TyKind,
TyPatKind,
UnOp,
@ -835,8 +834,8 @@ macro_rules! common_visitor_and_walkers {
visit_visitable!($($mut)? vis, impl_),
ItemKind::Trait(trait_) =>
visit_visitable!($($mut)? vis, trait_),
ItemKind::TraitAlias(ident, generics, bounds) => {
visit_visitable!($($mut)? vis, ident, generics);
ItemKind::TraitAlias(box TraitAlias { constness, ident, generics, bounds}) => {
visit_visitable!($($mut)? vis, constness, ident, generics);
visit_visitable_with!($($mut)? vis, bounds, BoundKind::Bound)
}
ItemKind::MacCall(m) =>

View file

@ -311,3 +311,10 @@ pub enum Pinnedness {
Not,
Pinned,
}
impl Pinnedness {
/// Return `true` if self is pinned
pub fn is_pinned(self) -> bool {
matches!(self, Self::Pinned)
}
}

View file

@ -27,7 +27,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
}
fn lower_stmts(
pub(super) fn lower_stmts(
&mut self,
mut ast_stmts: &[Stmt],
) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {

View file

@ -0,0 +1,345 @@
use std::sync::Arc;
use thin_vec::thin_vec;
use crate::LoweringContext;
impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// Lowered contracts are guarded with the `contract_checks` compiler flag,
/// i.e. the flag turns into a boolean guard in the lowered HIR. The reason
/// for not eliminating the contract code entirely when the `contract_checks`
/// flag is disabled is so that contracts can be type checked, even when
/// they are disabled, which avoids them becoming stale (i.e. out of sync
/// with the codebase) over time.
///
/// The optimiser should be able to eliminate all contract code guarded
/// by `if false`, leaving the original body intact when runtime contract
/// checks are disabled.
pub(super) fn lower_contract(
&mut self,
body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
contract: &rustc_ast::FnContract,
) -> rustc_hir::Expr<'hir> {
// The order in which things are lowered is important! I.e to
// refer to variables in contract_decls from postcond/precond,
// we must lower it first!
let contract_decls = self.lower_stmts(&contract.declarations).0;
match (&contract.requires, &contract.ensures) {
(Some(req), Some(ens)) => {
// Lower the fn contract, which turns:
//
// { body }
//
// into:
//
// let __postcond = if contract_checks {
// CONTRACT_DECLARATIONS;
// contract_check_requires(PRECOND);
// Some(|ret_val| POSTCOND)
// } else {
// None
// };
// {
// let ret = { body };
//
// if contract_checks {
// contract_check_ensures(__postcond, ret)
// } else {
// ret
// }
// }
let precond = self.lower_precond(req);
let postcond_checker = self.lower_postcond_checker(ens);
let contract_check = self.lower_contract_check_with_postcond(
contract_decls,
Some(precond),
postcond_checker,
);
let wrapped_body =
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
self.expr_block(wrapped_body)
}
(None, Some(ens)) => {
// Lower the fn contract, which turns:
//
// { body }
//
// into:
//
// let __postcond = if contract_checks {
// Some(|ret_val| POSTCOND)
// } else {
// None
// };
// {
// let ret = { body };
//
// if contract_checks {
// CONTRACT_DECLARATIONS;
// contract_check_ensures(__postcond, ret)
// } else {
// ret
// }
// }
let postcond_checker = self.lower_postcond_checker(ens);
let contract_check =
self.lower_contract_check_with_postcond(contract_decls, None, postcond_checker);
let wrapped_body =
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
self.expr_block(wrapped_body)
}
(Some(req), None) => {
// Lower the fn contract, which turns:
//
// { body }
//
// into:
//
// {
// if contracts_checks {
// CONTRACT_DECLARATIONS;
// contract_requires(PRECOND);
// }
// body
// }
let precond = self.lower_precond(req);
let precond_check = self.lower_contract_check_just_precond(contract_decls, precond);
let body = self.arena.alloc(body(self));
// Flatten the body into precond check, then body.
let wrapped_body = self.block_all(
body.span,
self.arena.alloc_from_iter([precond_check].into_iter()),
Some(body),
);
self.expr_block(wrapped_body)
}
(None, None) => body(self),
}
}
/// Lower the precondition check intrinsic.
fn lower_precond(&mut self, req: &Box<rustc_ast::Expr>) -> rustc_hir::Stmt<'hir> {
let lowered_req = self.lower_expr_mut(&req);
let req_span = self.mark_span_with_reason(
rustc_span::DesugaringKind::Contract,
lowered_req.span,
Some(Arc::clone(&self.allow_contracts)),
);
let precond = self.expr_call_lang_item_fn_mut(
req_span,
rustc_hir::LangItem::ContractCheckRequires,
&*arena_vec![self; lowered_req],
);
self.stmt_expr(req.span, precond)
}
fn lower_postcond_checker(
&mut self,
ens: &Box<rustc_ast::Expr>,
) -> &'hir rustc_hir::Expr<'hir> {
let ens_span = self.lower_span(ens.span);
let ens_span = self.mark_span_with_reason(
rustc_span::DesugaringKind::Contract,
ens_span,
Some(Arc::clone(&self.allow_contracts)),
);
let lowered_ens = self.lower_expr_mut(&ens);
self.expr_call_lang_item_fn(
ens_span,
rustc_hir::LangItem::ContractBuildCheckEnsures,
&*arena_vec![self; lowered_ens],
)
}
fn lower_contract_check_just_precond(
&mut self,
contract_decls: &'hir [rustc_hir::Stmt<'hir>],
precond: rustc_hir::Stmt<'hir>,
) -> rustc_hir::Stmt<'hir> {
let stmts = self
.arena
.alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain([precond].into_iter()));
let then_block_stmts = self.block_all(precond.span, stmts, None);
let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
let precond_check = rustc_hir::ExprKind::If(
self.arena.alloc(self.expr_bool_literal(precond.span, self.tcx.sess.contract_checks())),
then_block,
None,
);
let precond_check = self.expr(precond.span, precond_check);
self.stmt_expr(precond.span, precond_check)
}
fn lower_contract_check_with_postcond(
&mut self,
contract_decls: &'hir [rustc_hir::Stmt<'hir>],
precond: Option<rustc_hir::Stmt<'hir>>,
postcond_checker: &'hir rustc_hir::Expr<'hir>,
) -> &'hir rustc_hir::Expr<'hir> {
let stmts = self
.arena
.alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain(precond.into_iter()));
let span = match precond {
Some(precond) => precond.span,
None => postcond_checker.span,
};
let postcond_checker = self.arena.alloc(self.expr_enum_variant_lang_item(
postcond_checker.span,
rustc_hir::lang_items::LangItem::OptionSome,
&*arena_vec![self; *postcond_checker],
));
let then_block_stmts = self.block_all(span, stmts, Some(postcond_checker));
let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
let none_expr = self.arena.alloc(self.expr_enum_variant_lang_item(
postcond_checker.span,
rustc_hir::lang_items::LangItem::OptionNone,
Default::default(),
));
let else_block = self.block_expr(none_expr);
let else_block = self.arena.alloc(self.expr_block(else_block));
let contract_check = rustc_hir::ExprKind::If(
self.arena.alloc(self.expr_bool_literal(span, self.tcx.sess.contract_checks())),
then_block,
Some(else_block),
);
self.arena.alloc(self.expr(span, contract_check))
}
fn wrap_body_with_contract_check(
&mut self,
body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
contract_check: &'hir rustc_hir::Expr<'hir>,
postcond_span: rustc_span::Span,
) -> &'hir rustc_hir::Block<'hir> {
let check_ident: rustc_span::Ident =
rustc_span::Ident::from_str_and_span("__ensures_checker", postcond_span);
let (check_hir_id, postcond_decl) = {
// Set up the postcondition `let` statement.
let (checker_pat, check_hir_id) = self.pat_ident_binding_mode_mut(
postcond_span,
check_ident,
rustc_hir::BindingMode::NONE,
);
(
check_hir_id,
self.stmt_let_pat(
None,
postcond_span,
Some(contract_check),
self.arena.alloc(checker_pat),
rustc_hir::LocalSource::Contract,
),
)
};
// Install contract_ensures so we will intercept `return` statements,
// then lower the body.
self.contract_ensures = Some((postcond_span, check_ident, check_hir_id));
let body = self.arena.alloc(body(self));
// Finally, inject an ensures check on the implicit return of the body.
let body = self.inject_ensures_check(body, postcond_span, check_ident, check_hir_id);
// Flatten the body into precond, then postcond, then wrapped body.
let wrapped_body = self.block_all(
body.span,
self.arena.alloc_from_iter([postcond_decl].into_iter()),
Some(body),
);
wrapped_body
}
/// Create an `ExprKind::Ret` that is optionally wrapped by a call to check
/// a contract ensures clause, if it exists.
pub(super) fn checked_return(
&mut self,
opt_expr: Option<&'hir rustc_hir::Expr<'hir>>,
) -> rustc_hir::ExprKind<'hir> {
let checked_ret =
if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures {
let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span));
Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id))
} else {
opt_expr
};
rustc_hir::ExprKind::Ret(checked_ret)
}
/// Wraps an expression with a call to the ensures check before it gets returned.
pub(super) fn inject_ensures_check(
&mut self,
expr: &'hir rustc_hir::Expr<'hir>,
span: rustc_span::Span,
cond_ident: rustc_span::Ident,
cond_hir_id: rustc_hir::HirId,
) -> &'hir rustc_hir::Expr<'hir> {
// {
// let ret = { body };
//
// if contract_checks {
// contract_check_ensures(__postcond, ret)
// } else {
// ret
// }
// }
let ret_ident: rustc_span::Ident = rustc_span::Ident::from_str_and_span("__ret", span);
// Set up the return `let` statement.
let (ret_pat, ret_hir_id) =
self.pat_ident_binding_mode_mut(span, ret_ident, rustc_hir::BindingMode::NONE);
let ret_stmt = self.stmt_let_pat(
None,
span,
Some(expr),
self.arena.alloc(ret_pat),
rustc_hir::LocalSource::Contract,
);
let ret = self.expr_ident(span, ret_ident, ret_hir_id);
let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id);
let contract_check = self.expr_call_lang_item_fn_mut(
span,
rustc_hir::LangItem::ContractCheckEnsures,
arena_vec![self; *cond_fn, *ret],
);
let contract_check = self.arena.alloc(contract_check);
let call_expr = self.block_expr_block(contract_check);
// same ident can't be used in 2 places, so we create a new one for the
// else branch
let ret = self.expr_ident(span, ret_ident, ret_hir_id);
let ret_block = self.block_expr_block(ret);
let contracts_enabled: rustc_hir::Expr<'_> =
self.expr_bool_literal(span, self.tcx.sess.contract_checks());
let contract_check = self.arena.alloc(self.expr(
span,
rustc_hir::ExprKind::If(
self.arena.alloc(contracts_enabled),
call_expr,
Some(ret_block),
),
));
let attrs: rustc_ast::AttrVec = thin_vec![self.unreachable_code_attr(span)];
self.lower_attrs(contract_check.hir_id, &attrs, span, rustc_hir::Target::Expression);
let ret_block = self.block_all(span, arena_vec![self; ret_stmt], Some(contract_check));
self.arena.alloc(self.expr_block(self.arena.alloc(ret_block)))
}
}

View file

@ -7,6 +7,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::definitions::DefPathData;
use rustc_hir::{HirId, Target, find_attr};
use rustc_middle::span_bug;
use rustc_middle::ty::TyCtxt;
@ -62,13 +63,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
ensure_sufficient_stack(|| {
let mut span = self.lower_span(e.span);
match &e.kind {
// Parenthesis expression does not have a HirId and is handled specially.
ExprKind::Paren(ex) => {
let mut ex = self.lower_expr_mut(ex);
// Include parens in span, but only if it is a super-span.
if e.span.contains(ex.span) {
ex.span = self.lower_span(e.span);
ex.span = self.lower_span(e.span.with_ctxt(ex.span.ctxt()));
}
// Merge attributes into the inner expression.
if !e.attrs.is_empty() {
@ -287,7 +289,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_span(*brackets_span),
),
ExprKind::Range(e1, e2, lims) => {
self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims)
span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None);
self.lower_expr_range(span, e1.as_deref(), e2.as_deref(), *lims)
}
ExprKind::Underscore => {
let guar = self.dcx().emit_err(UnderscoreExprLhsAssign { span: e.span });
@ -379,40 +382,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span),
};
hir::Expr { hir_id: expr_hir_id, kind, span: self.lower_span(e.span) }
hir::Expr { hir_id: expr_hir_id, kind, span }
})
}
/// Create an `ExprKind::Ret` that is optionally wrapped by a call to check
/// a contract ensures clause, if it exists.
fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> {
let checked_ret =
if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures {
let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span));
Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id))
} else {
opt_expr
};
hir::ExprKind::Ret(checked_ret)
}
/// Wraps an expression with a call to the ensures check before it gets returned.
pub(crate) fn inject_ensures_check(
&mut self,
expr: &'hir hir::Expr<'hir>,
span: Span,
cond_ident: Ident,
cond_hir_id: HirId,
) -> &'hir hir::Expr<'hir> {
let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id);
let call_expr = self.expr_call_lang_item_fn_mut(
span,
hir::LangItem::ContractCheckEnsures,
arena_vec![self; *cond_fn, *expr],
);
self.arena.alloc(call_expr)
}
pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
@ -491,7 +464,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
for (idx, arg) in args.iter().cloned().enumerate() {
if legacy_args_idx.contains(&idx) {
let node_id = self.next_node_id();
self.create_def(node_id, None, DefKind::AnonConst, f.span);
self.create_def(
node_id,
None,
DefKind::AnonConst,
DefPathData::LateAnonConst,
f.span,
);
let mut visitor = WillCreateDefIdsVisitor {};
let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) {
Box::new(Expr {
@ -886,6 +865,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
};
let features = match await_kind {
FutureKind::Future if is_async_gen => Some(Arc::clone(&self.allow_async_gen)),
FutureKind::Future => None,
FutureKind::AsyncIterator => Some(Arc::clone(&self.allow_for_await)),
};
@ -1263,13 +1243,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
let rhs = self.lower_expr(rhs);
// Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`.
let destructure_let = self.stmt_let_pat(
None,
whole_span,
Some(rhs),
pat,
hir::LocalSource::AssignDesugar(self.lower_span(eq_sign_span)),
);
let destructure_let =
self.stmt_let_pat(None, whole_span, Some(rhs), pat, hir::LocalSource::AssignDesugar);
// `a = lhs1; b = lhs2;`.
let stmts = self.arena.alloc_from_iter(std::iter::once(destructure_let).chain(assignments));
@ -1505,7 +1480,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
let e1 = self.lower_expr_mut(e1);
let e2 = self.lower_expr_mut(e2);
let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, self.lower_span(span));
let fn_path = self.make_lang_item_qpath(hir::LangItem::RangeInclusiveNew, span, None);
let fn_expr = self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path)));
hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2])
}
@ -1582,14 +1557,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}))
.map(|(s, e)| {
let span = self.lower_span(e.span);
let span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None);
let expr = self.lower_expr(e);
let ident = Ident::new(s, self.lower_span(e.span));
self.expr_field(ident, expr, e.span)
let ident = Ident::new(s, span);
self.expr_field(ident, expr, span)
}),
);
hir::ExprKind::Struct(
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
self.arena.alloc(self.make_lang_item_qpath(lang_item, span, None)),
fields,
hir::StructTailExpr::None,
)
@ -1739,8 +1716,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `yield $expr` is transformed into `task_context = yield async_gen_ready($expr)`.
// This ensures that we store our resumed `ResumeContext` correctly, and also that
// the apparent value of the `yield` expression is `()`.
let wrapped_yielded = self.expr_call_lang_item_fn(
let desugar_span = self.mark_span_with_reason(
DesugaringKind::Async,
span,
Some(Arc::clone(&self.allow_async_gen)),
);
let wrapped_yielded = self.expr_call_lang_item_fn(
desugar_span,
hir::LangItem::AsyncGenReady,
std::slice::from_ref(yielded),
);
@ -1752,7 +1734,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
unreachable!("use of `await` outside of an async context.");
};
let task_context_ident = Ident::with_dummy_span(sym::_task_context);
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
let lhs = self.expr_ident(desugar_span, task_context_ident, task_context_hid);
hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span))
} else {
@ -1971,16 +1953,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
};
// `#[allow(unreachable_code)]`
let attr = attr::mk_attr_nested_word(
&self.tcx.sess.psess.attr_id_generator,
AttrStyle::Outer,
Safety::Default,
sym::allow,
sym::unreachable_code,
try_span,
);
let attrs: AttrVec = thin_vec![attr];
let attrs: AttrVec = thin_vec![self.unreachable_code_attr(try_span)];
// `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,`
let continue_arm = {
@ -2120,7 +2093,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e))
}
fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> {
pub(super) fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> {
self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[])))
}
@ -2161,6 +2134,43 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(span, hir::ExprKind::Call(e, args))
}
pub(super) fn expr_struct(
&mut self,
span: Span,
path: &'hir hir::QPath<'hir>,
fields: &'hir [hir::ExprField<'hir>],
) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Struct(path, fields, rustc_hir::StructTailExpr::None))
}
pub(super) fn expr_enum_variant(
&mut self,
span: Span,
path: &'hir hir::QPath<'hir>,
fields: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
let fields = self.arena.alloc_from_iter(fields.into_iter().enumerate().map(|(i, f)| {
hir::ExprField {
hir_id: self.next_id(),
ident: Ident::from_str(&i.to_string()),
expr: f,
span: f.span,
is_shorthand: false,
}
}));
self.expr_struct(span, path, fields)
}
pub(super) fn expr_enum_variant_lang_item(
&mut self,
span: Span,
lang_item: hir::LangItem,
fields: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
let path = self.arena.alloc(self.make_lang_item_qpath(lang_item, span, None));
self.expr_enum_variant(span, path, fields)
}
pub(super) fn expr_call(
&mut self,
span: Span,
@ -2189,8 +2199,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args))
}
fn expr_lang_item_path(&mut self, span: Span, lang_item: hir::LangItem) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span))))
pub(super) fn expr_lang_item_path(
&mut self,
span: Span,
lang_item: hir::LangItem,
) -> hir::Expr<'hir> {
let qpath = self.make_lang_item_qpath(lang_item, self.lower_span(span), None);
self.expr(span, hir::ExprKind::Path(qpath))
}
/// `<LangItem>::name`
@ -2270,6 +2285,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(b.span, hir::ExprKind::Block(b, None))
}
/// Wrap an expression in a block, and wrap that block in an expression again.
/// Useful for constructing if-expressions, which require expressions of
/// kind block.
pub(super) fn block_expr_block(
&mut self,
expr: &'hir hir::Expr<'hir>,
) -> &'hir hir::Expr<'hir> {
let b = self.block_expr(expr);
self.arena.alloc(self.expr_block(b))
}
pub(super) fn expr_array_ref(
&mut self,
span: Span,
@ -2283,6 +2309,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr))
}
pub(super) fn expr_bool_literal(&mut self, span: Span, val: bool) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Lit(Spanned { node: LitKind::Bool(val), span }))
}
pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {
let hir_id = self.next_id();
hir::Expr { hir_id, kind, span: self.lower_span(span) }
@ -2316,6 +2346,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
body: expr,
}
}
/// `#[allow(unreachable_code)]`
pub(super) fn unreachable_code_attr(&mut self, span: Span) -> Attribute {
let attr = attr::mk_attr_nested_word(
&self.tcx.sess.psess.attr_id_generator,
AttrStyle::Outer,
Safety::Default,
sym::allow,
sym::unreachable_code,
span,
);
attr
}
}
/// Used by [`LoweringContext::make_lowered_await`] to customize the desugaring based on what kind

View file

@ -391,7 +391,8 @@ fn make_format_spec<'hir>(
let flags = ctx.expr_field(Ident::new(sym::flags, sp), ctx.arena.alloc(flags), sp);
let precision = ctx.expr_field(Ident::new(sym::precision, sp), ctx.arena.alloc(precision), sp);
let width = ctx.expr_field(Ident::new(sym::width, sp), ctx.arena.alloc(width), sp);
let placeholder = ctx.arena.alloc(hir::QPath::LangItem(hir::LangItem::FormatPlaceholder, sp));
let placeholder =
ctx.arena.alloc(ctx.make_lang_item_qpath(hir::LangItem::FormatPlaceholder, sp, None));
let fields = ctx.arena.alloc_from_iter([position, flags, precision, width]);
ctx.expr(sp, hir::ExprKind::Struct(placeholder, fields, hir::StructTailExpr::None))
}

View file

@ -36,20 +36,18 @@ pub(super) struct ItemLowerer<'a, 'hir> {
/// clause if it exists.
fn add_ty_alias_where_clause(
generics: &mut ast::Generics,
mut where_clauses: TyAliasWhereClauses,
after_where_clause: &ast::WhereClause,
prefer_first: bool,
) {
generics.where_clause.predicates.extend_from_slice(&after_where_clause.predicates);
let mut before = (generics.where_clause.has_where_token, generics.where_clause.span);
let mut after = (after_where_clause.has_where_token, after_where_clause.span);
if !prefer_first {
(where_clauses.before, where_clauses.after) = (where_clauses.after, where_clauses.before);
(before, after) = (after, before);
}
let where_clause =
if where_clauses.before.has_where_token || !where_clauses.after.has_where_token {
where_clauses.before
} else {
where_clauses.after
};
generics.where_clause.has_where_token = where_clause.has_where_token;
generics.where_clause.span = where_clause.span;
(generics.where_clause.has_where_token, generics.where_clause.span) =
if before.0 || !after.0 { before } else { after };
}
impl<'a, 'hir> ItemLowerer<'a, 'hir> {
@ -271,7 +269,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_body(|this| (&[], this.expr(span, hir::ExprKind::InlineAsm(asm))));
hir::ItemKind::GlobalAsm { asm, fake_body }
}
ItemKind::TyAlias(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
ItemKind::TyAlias(box TyAlias { ident, generics, after_where_clause, ty, .. }) => {
// We lower
//
// type Foo = impl Trait
@ -282,7 +280,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// opaque type Foo1: Trait
let ident = self.lower_ident(*ident);
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, true);
add_ty_alias_where_clause(&mut generics, after_where_clause, true);
let (generics, ty) = self.lower_generics(
&generics,
id,
@ -417,7 +415,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
hir::ItemKind::Trait(constness, *is_auto, safety, ident, generics, bounds, items)
}
ItemKind::TraitAlias(ident, generics, bounds) => {
ItemKind::TraitAlias(box TraitAlias { constness, ident, generics, bounds }) => {
let constness = self.lower_constness(*constness);
let ident = self.lower_ident(*ident);
let (generics, bounds) = self.lower_generics(
generics,
@ -426,12 +425,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|this| {
this.lower_param_bounds(
bounds,
RelaxedBoundPolicy::Allowed,
RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::TraitAlias),
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
)
},
);
hir::ItemKind::TraitAlias(ident, generics, bounds)
hir::ItemKind::TraitAlias(constness, ident, generics, bounds)
}
ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
let ident = self.lower_ident(*ident);
@ -901,10 +900,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}
AssocItemKind::Type(box TyAlias {
ident, generics, where_clauses, bounds, ty, ..
ident,
generics,
after_where_clause,
bounds,
ty,
..
}) => {
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
add_ty_alias_where_clause(&mut generics, after_where_clause, false);
let (generics, kind) = self.lower_generics(
&generics,
i.id,
@ -1070,9 +1074,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
(*ident, (generics, hir::ImplItemKind::Fn(sig, body_id)))
}
AssocItemKind::Type(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
AssocItemKind::Type(box TyAlias {
ident, generics, after_where_clause, ty, ..
}) => {
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
add_ty_alias_where_clause(&mut generics, after_where_clause, false);
(
*ident,
self.lower_generics(
@ -1214,76 +1220,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let params =
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
// Optionally lower the fn contract, which turns:
//
// { body }
//
// into:
//
// { contract_requires(PRECOND); let __postcond = |ret_val| POSTCOND; postcond({ body }) }
// Optionally lower the fn contract
if let Some(contract) = contract {
let precond = if let Some(req) = &contract.requires {
// Lower the precondition check intrinsic.
let lowered_req = this.lower_expr_mut(&req);
let req_span = this.mark_span_with_reason(
DesugaringKind::Contract,
lowered_req.span,
None,
);
let precond = this.expr_call_lang_item_fn_mut(
req_span,
hir::LangItem::ContractCheckRequires,
&*arena_vec![this; lowered_req],
);
Some(this.stmt_expr(req.span, precond))
} else {
None
};
let (postcond, body) = if let Some(ens) = &contract.ensures {
let ens_span = this.lower_span(ens.span);
let ens_span =
this.mark_span_with_reason(DesugaringKind::Contract, ens_span, None);
// Set up the postcondition `let` statement.
let check_ident: Ident =
Ident::from_str_and_span("__ensures_checker", ens_span);
let (checker_pat, check_hir_id) = this.pat_ident_binding_mode_mut(
ens_span,
check_ident,
hir::BindingMode::NONE,
);
let lowered_ens = this.lower_expr_mut(&ens);
let postcond_checker = this.expr_call_lang_item_fn(
ens_span,
hir::LangItem::ContractBuildCheckEnsures,
&*arena_vec![this; lowered_ens],
);
let postcond = this.stmt_let_pat(
None,
ens_span,
Some(postcond_checker),
this.arena.alloc(checker_pat),
hir::LocalSource::Contract,
);
// Install contract_ensures so we will intercept `return` statements,
// then lower the body.
this.contract_ensures = Some((ens_span, check_ident, check_hir_id));
let body = this.arena.alloc(body(this));
// Finally, inject an ensures check on the implicit return of the body.
let body = this.inject_ensures_check(body, ens_span, check_ident, check_hir_id);
(Some(postcond), body)
} else {
let body = &*this.arena.alloc(body(this));
(None, body)
};
// Flatten the body into precond, then postcond, then wrapped body.
let wrapped_body = this.block_all(
body.span,
this.arena.alloc_from_iter([precond, postcond].into_iter().flatten()),
Some(body),
);
(params, this.expr_block(wrapped_body))
(params, this.lower_contract(body, contract))
} else {
(params, body(this))
}

View file

@ -51,6 +51,7 @@ use rustc_data_structures::tagged_ptr::TaggedRef;
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::definitions::{DefPathData, DisambiguatorState};
use rustc_hir::lints::DelayedLint;
use rustc_hir::{
self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource,
@ -77,6 +78,7 @@ macro_rules! arena_vec {
mod asm;
mod block;
mod contract;
mod delegation;
mod errors;
mod expr;
@ -92,6 +94,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
struct LoweringContext<'a, 'hir> {
tcx: TyCtxt<'hir>,
resolver: &'a mut ResolverAstLowering,
disambiguator: DisambiguatorState,
/// Used to allocate HIR nodes.
arena: &'hir hir::Arena<'hir>,
@ -135,9 +138,11 @@ struct LoweringContext<'a, 'hir> {
#[cfg(debug_assertions)]
node_id_to_local_id: NodeMap<hir::ItemLocalId>,
allow_contracts: Arc<[Symbol]>,
allow_try_trait: Arc<[Symbol]>,
allow_gen_future: Arc<[Symbol]>,
allow_pattern_type: Arc<[Symbol]>,
allow_async_gen: Arc<[Symbol]>,
allow_async_iterator: Arc<[Symbol]>,
allow_for_await: Arc<[Symbol]>,
allow_async_fn_traits: Arc<[Symbol]>,
@ -154,6 +159,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Pseudo-globals.
tcx,
resolver,
disambiguator: DisambiguatorState::new(),
arena: tcx.hir_arena,
// HirId handling.
@ -179,6 +185,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
current_item: None,
impl_trait_defs: Vec::new(),
impl_trait_bounds: Vec::new(),
allow_contracts: [sym::contracts_internals].into(),
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(),
allow_gen_future: if tcx.features().async_fn_track_caller() {
@ -186,8 +193,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} else {
[sym::gen_future].into()
},
allow_for_await: [sym::async_iterator].into(),
allow_for_await: [sym::async_gen_internals, sym::async_iterator].into(),
allow_async_fn_traits: [sym::async_fn_traits].into(),
allow_async_gen: [sym::async_gen_internals].into(),
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
// interact with `gen`/`async gen` blocks
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
@ -296,6 +304,7 @@ enum RelaxedBoundPolicy<'a> {
enum RelaxedBoundForbiddenReason {
TraitObjectTy,
SuperTrait,
TraitAlias,
AssocTyBounds,
LateBoundVarsInScope,
}
@ -544,6 +553,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
node_id: ast::NodeId,
name: Option<Symbol>,
def_kind: DefKind,
def_path_data: DefPathData,
span: Span,
) -> LocalDefId {
let parent = self.current_hir_id_owner.def_id;
@ -559,7 +569,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let def_id = self
.tcx
.at(span)
.create_def(parent, name, def_kind, None, &mut self.resolver.disambiguator)
.create_def(parent, name, def_kind, Some(def_path_data), &mut self.disambiguator)
.def_id();
debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
@ -844,6 +854,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
param,
Some(kw::UnderscoreLifetime),
DefKind::LifetimeParam,
DefPathData::DesugaredAnonymousLifetime,
ident.span,
);
debug!(?_def_id);
@ -2085,12 +2096,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: Span,
rbp: RelaxedBoundPolicy<'_>,
) {
// Even though feature `more_maybe_bounds` bypasses the given policy and (currently) enables
// relaxed bounds in every conceivable position[^1], we don't want to advertise it to the user
// (via a feature gate) since it's super internal. Besides this, it'd be quite distracting.
// Even though feature `more_maybe_bounds` enables the user to relax all default bounds
// other than `Sized` in a lot more positions (thereby bypassing the given policy), we don't
// want to advertise it to the user (via a feature gate error) since it's super internal.
//
// [^1]: Strictly speaking, this is incorrect (at the very least for `Sized`) because it's
// no longer fully consistent with default trait elaboration in HIR ty lowering.
// FIXME(more_maybe_bounds): Moreover, if we actually were to add proper default traits
// (like a hypothetical `Move` or `Leak`) we would want to validate the location according
// to default trait elaboration in HIR ty lowering (which depends on the specific trait in
// question: E.g., `?Sized` & `?Move` most likely won't be allowed in all the same places).
match rbp {
RelaxedBoundPolicy::Allowed => return,
@ -2103,33 +2116,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
RelaxedBoundPolicy::Forbidden(reason) => {
let gate = |context, subject| {
let extended = self.tcx.features().more_maybe_bounds();
let is_sized = trait_ref
.trait_def_id()
.is_some_and(|def_id| self.tcx.is_lang_item(def_id, hir::LangItem::Sized));
if extended && !is_sized {
return;
}
let prefix = if extended { "`Sized` " } else { "" };
let mut diag = self.dcx().struct_span_err(
span,
format!("relaxed {prefix}bounds are not permitted in {context}"),
);
if is_sized {
diag.note(format!(
"{subject} are not implicitly bounded by `Sized`, \
so there is nothing to relax"
));
}
diag.emit();
};
match reason {
RelaxedBoundForbiddenReason::TraitObjectTy => {
if self.tcx.features().more_maybe_bounds() {
return;
}
self.dcx().span_err(
span,
"relaxed bounds are not permitted in trait object types",
);
gate("trait object types", "trait object types");
return;
}
RelaxedBoundForbiddenReason::SuperTrait => {
if self.tcx.features().more_maybe_bounds() {
return;
}
let mut diag = self.dcx().struct_span_err(
span,
"relaxed bounds are not permitted in supertrait bounds",
);
if let Some(def_id) = trait_ref.trait_def_id()
&& self.tcx.is_lang_item(def_id, hir::LangItem::Sized)
{
diag.note("traits are `?Sized` by default");
}
diag.emit();
gate("supertrait bounds", "traits");
return;
}
RelaxedBoundForbiddenReason::TraitAlias => {
gate("trait alias bounds", "trait aliases");
return;
}
RelaxedBoundForbiddenReason::AssocTyBounds
@ -2142,7 +2163,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.struct_span_err(span, "this relaxed bound is not permitted here")
.with_note(
"in this context, relaxed bounds are only allowed on \
type parameters defined by the closest item",
type parameters defined on the closest item",
)
.emit();
}
@ -2278,7 +2299,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// We're lowering a const argument that was originally thought to be a type argument,
// so the def collector didn't create the def ahead of time. That's why we have to do
// it here.
let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);
let def_id = self.create_def(
node_id,
None,
DefKind::AnonConst,
DefPathData::LateAnonConst,
span,
);
let hir_id = self.lower_node_id(node_id);
let path_expr = Expr {
@ -2508,8 +2535,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
lang_item: hir::LangItem,
fields: &'hir [hir::PatField<'hir>],
) -> &'hir hir::Pat<'hir> {
let qpath = hir::QPath::LangItem(lang_item, self.lower_span(span));
self.pat(span, hir::PatKind::Struct(qpath, fields, None))
let path = self.make_lang_item_qpath(lang_item, self.lower_span(span), None);
self.pat(span, hir::PatKind::Struct(path, fields, None))
}
fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, HirId) {

View file

@ -3,6 +3,7 @@ use std::sync::Arc;
use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::definitions::DefPathData;
use rustc_hir::{self as hir, LangItem, Target};
use rustc_middle::span_bug;
use rustc_span::source_map::{Spanned, respan};
@ -143,7 +144,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
// return inner to be processed in next loop
PatKind::Paren(inner) => pattern = inner,
PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
PatKind::MacCall(_) => {
panic!("{pattern:#?} shouldn't exist here")
}
PatKind::Err(guar) => break hir::PatKind::Err(*guar),
}
};
@ -154,7 +157,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_pat_tuple(
&mut self,
pats: &[Box<Pat>],
pats: &[Pat],
ctx: &str,
) -> (&'hir [hir::Pat<'hir>], hir::DotDotPos) {
let mut elems = Vec::with_capacity(pats.len());
@ -209,7 +212,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// When encountering `($binding_mode $ident @)? ..` (`slice`),
/// this is interpreted as a sub-slice pattern semantically.
/// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
fn lower_pat_slice(&mut self, pats: &[Box<Pat>]) -> hir::PatKind<'hir> {
fn lower_pat_slice(&mut self, pats: &[Pat]) -> hir::PatKind<'hir> {
let mut before = Vec::new();
let mut after = Vec::new();
let mut slice = None;
@ -460,6 +463,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
)
}),
),
TyPatKind::NotNull => hir::TyPatKind::NotNull,
TyPatKind::Or(variants) => {
hir::TyPatKind::Or(self.arena.alloc_from_iter(
variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)),
@ -524,7 +528,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// We're generating a range end that didn't exist in the AST,
// so the def collector didn't create the def ahead of time. That's why we have to do
// it here.
let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);
let def_id =
self.create_def(node_id, None, DefKind::AnonConst, DefPathData::LateAnonConst, span);
let hir_id = self.lower_node_id(node_id);
let unstable_span = self.mark_span_with_reason(

View file

@ -145,25 +145,24 @@ impl<'a> AstValidator<'a> {
&mut self,
ty_alias: &TyAlias,
) -> Result<(), errors::WhereClauseBeforeTypeAlias> {
if ty_alias.ty.is_none() || !ty_alias.where_clauses.before.has_where_token {
if ty_alias.ty.is_none() || !ty_alias.generics.where_clause.has_where_token {
return Ok(());
}
let (before_predicates, after_predicates) =
ty_alias.generics.where_clause.predicates.split_at(ty_alias.where_clauses.split);
let span = ty_alias.where_clauses.before.span;
let span = ty_alias.generics.where_clause.span;
let sugg = if !before_predicates.is_empty() || !ty_alias.where_clauses.after.has_where_token
let sugg = if !ty_alias.generics.where_clause.predicates.is_empty()
|| !ty_alias.after_where_clause.has_where_token
{
let mut state = State::new();
if !ty_alias.where_clauses.after.has_where_token {
if !ty_alias.after_where_clause.has_where_token {
state.space();
state.word_space("where");
}
let mut first = after_predicates.is_empty();
for p in before_predicates {
let mut first = ty_alias.after_where_clause.predicates.is_empty();
for p in &ty_alias.generics.where_clause.predicates {
if !first {
state.word_space(",");
}
@ -174,7 +173,7 @@ impl<'a> AstValidator<'a> {
errors::WhereClauseBeforeTypeAliasSugg::Move {
left: span,
snippet: state.s.eof(),
right: ty_alias.where_clauses.after.span.shrink_to_hi(),
right: ty_alias.after_where_clause.span.shrink_to_hi(),
}
} else {
errors::WhereClauseBeforeTypeAliasSugg::Remove { span }
@ -566,11 +565,7 @@ impl<'a> AstValidator<'a> {
self.dcx().emit_err(errors::BoundInContext { span, ctx });
}
fn check_foreign_ty_genericless(
&self,
generics: &Generics,
where_clauses: &TyAliasWhereClauses,
) {
fn check_foreign_ty_genericless(&self, generics: &Generics, after_where_clause: &WhereClause) {
let cannot_have = |span, descr, remove_descr| {
self.dcx().emit_err(errors::ExternTypesCannotHave {
span,
@ -584,14 +579,14 @@ impl<'a> AstValidator<'a> {
cannot_have(generics.span, "generic parameters", "generic parameters");
}
let check_where_clause = |where_clause: TyAliasWhereClause| {
let check_where_clause = |where_clause: &WhereClause| {
if where_clause.has_where_token {
cannot_have(where_clause.span, "`where` clauses", "`where` clause");
}
};
check_where_clause(where_clauses.before);
check_where_clause(where_clauses.after);
check_where_clause(&generics.where_clause);
check_where_clause(&after_where_clause);
}
fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option<Span>) {
@ -1197,6 +1192,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
});
}
ItemKind::TraitAlias(box TraitAlias { constness, generics, bounds, .. }) => {
let disallowed = matches!(constness, ast::Const::No)
.then(|| TildeConstReason::Trait { span: item.span });
self.with_tilde_const(disallowed, |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
});
}
ItemKind::Mod(safety, ident, mod_kind) => {
if let &Safety::Unsafe(span) = safety {
self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
@ -1261,7 +1264,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
visit::walk_item(self, item);
}
ItemKind::TyAlias(
ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. },
ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. },
) => {
self.check_defaultness(item.span, *defaultness);
if ty.is_none() {
@ -1276,9 +1279,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) {
self.dcx().emit_err(err);
}
} else if where_clauses.after.has_where_token {
} else if after_where_clause.has_where_token {
self.dcx().emit_err(errors::WhereClauseAfterTypeAlias {
span: where_clauses.after.span,
span: after_where_clause.span,
help: self.sess.is_nightly_build(),
});
}
@ -1308,7 +1311,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
..
@ -1316,7 +1319,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_defaultness(fi.span, *defaultness);
self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span));
self.check_type_no_bounds(bounds, "`extern` blocks");
self.check_foreign_ty_genericless(generics, where_clauses);
self.check_foreign_ty_genericless(generics, after_where_clause);
self.check_foreign_item_ascii_only(*ident);
}
ForeignItemKind::Static(box StaticItem { ident, safety, expr, .. }) => {

View file

@ -298,7 +298,7 @@ impl Printer {
}
}
// This is is where `BoxMarker`s are produced.
// This is where `BoxMarker`s are produced.
fn scan_begin(&mut self, token: BeginToken) -> BoxMarker {
if self.scan_stack.is_empty() {
self.left_total = 1;
@ -310,7 +310,7 @@ impl Printer {
BoxMarker
}
// This is is where `BoxMarker`s are consumed.
// This is where `BoxMarker`s are consumed.
fn scan_end(&mut self, b: BoxMarker) {
if self.scan_stack.is_empty() {
self.print_end();

View file

@ -1232,6 +1232,7 @@ impl<'a> State<'a> {
self.print_expr_anon_const(end, &[]);
}
}
rustc_ast::TyPatKind::NotNull => self.word("!null"),
rustc_ast::TyPatKind::Or(variants) => {
let mut first = true;
for pat in variants {
@ -1711,10 +1712,15 @@ impl<'a> State<'a> {
if mutbl.is_mut() {
self.word_nbsp("mut");
}
if let ByRef::Yes(rmutbl) = by_ref {
if let ByRef::Yes(pinnedness, rmutbl) = by_ref {
self.word_nbsp("ref");
if pinnedness.is_pinned() {
self.word_nbsp("pin");
}
if rmutbl.is_mut() {
self.word_nbsp("mut");
} else if pinnedness.is_pinned() {
self.word_nbsp("const");
}
}
self.print_ident(*ident);

View file

@ -1,7 +1,6 @@
use ast::StaticItem;
use itertools::{Itertools, Position};
use rustc_ast as ast;
use rustc_ast::ModKind;
use rustc_ast::{self as ast, ModKind, TraitAlias};
use rustc_span::Ident;
use crate::pp::BoxMarker;
@ -59,14 +58,14 @@ impl<'a> State<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
}) => {
self.print_associated_type(
*ident,
generics,
*where_clauses,
after_where_clause,
bounds,
ty.as_deref(),
vis,
@ -127,14 +126,12 @@ impl<'a> State<'a> {
&mut self,
ident: Ident,
generics: &ast::Generics,
where_clauses: ast::TyAliasWhereClauses,
after_where_clause: &ast::WhereClause,
bounds: &ast::GenericBounds,
ty: Option<&ast::Ty>,
vis: &ast::Visibility,
defaultness: ast::Defaultness,
) {
let (before_predicates, after_predicates) =
generics.where_clause.predicates.split_at(where_clauses.split);
let (cb, ib) = self.head("");
self.print_visibility(vis);
self.print_defaultness(defaultness);
@ -145,13 +142,13 @@ impl<'a> State<'a> {
self.word_nbsp(":");
self.print_type_bounds(bounds);
}
self.print_where_clause_parts(where_clauses.before.has_where_token, before_predicates);
self.print_where_clause(&generics.where_clause);
if let Some(ty) = ty {
self.space();
self.word_space("=");
self.print_type(ty);
}
self.print_where_clause_parts(where_clauses.after.has_where_token, after_predicates);
self.print_where_clause(&after_where_clause);
self.word(";");
self.end(ib);
self.end(cb);
@ -283,14 +280,14 @@ impl<'a> State<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
}) => {
self.print_associated_type(
*ident,
generics,
*where_clauses,
after_where_clause,
bounds,
ty.as_deref(),
&item.vis,
@ -388,8 +385,11 @@ impl<'a> State<'a> {
let empty = item.attrs.is_empty() && items.is_empty();
self.bclose(item.span, empty, cb);
}
ast::ItemKind::TraitAlias(ident, generics, bounds) => {
let (cb, ib) = self.head(visibility_qualified(&item.vis, "trait"));
ast::ItemKind::TraitAlias(box TraitAlias { constness, ident, generics, bounds }) => {
let (cb, ib) = self.head("");
self.print_visibility(&item.vis);
self.print_constness(*constness);
self.word_nbsp("trait");
self.print_ident(*ident);
self.print_generic_params(&generics.params);
self.nbsp();
@ -585,14 +585,14 @@ impl<'a> State<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
}) => {
self.print_associated_type(
*ident,
generics,
*where_clauses,
after_where_clause,
bounds,
ty.as_deref(),
vis,
@ -759,14 +759,7 @@ impl<'a> State<'a> {
}
fn print_where_clause(&mut self, where_clause: &ast::WhereClause) {
self.print_where_clause_parts(where_clause.has_where_token, &where_clause.predicates);
}
fn print_where_clause_parts(
&mut self,
has_where_token: bool,
predicates: &[ast::WherePredicate],
) {
let ast::WhereClause { has_where_token, ref predicates, span: _ } = *where_clause;
if predicates.is_empty() && !has_where_token {
return;
}

View file

@ -4,6 +4,8 @@ attr_parsing_as_needed_compatibility =
attr_parsing_bundle_needs_static =
linking modifier `bundle` is only compatible with `static` linking kind
attr_parsing_cfg_attr_bad_delim = wrong `cfg_attr` delimiters
attr_parsing_cfg_predicate_identifier =
`cfg` predicate key must be an identifier

View file

@ -1,19 +1,28 @@
use rustc_ast::{LitKind, NodeId};
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, NodeId, ast, token};
use rustc_errors::{Applicability, PResult};
use rustc_feature::{AttributeTemplate, Features, template};
use rustc_hir::RustcVersion;
use rustc_hir::attrs::CfgEntry;
use rustc_hir::{AttrPath, RustcVersion};
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::{exp, parse_in};
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::parse::feature_err;
use rustc_session::parse::{ParseSess, feature_err};
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;
use crate::context::{AcceptContext, ShouldEmit, Stage};
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser};
use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg,
};
use crate::{
CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
AttributeParser, CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics,
try_gate_cfg,
};
pub const CFG_TEMPLATE: AttributeTemplate = template!(
@ -21,7 +30,12 @@ pub const CFG_TEMPLATE: AttributeTemplate = template!(
"https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute"
);
pub fn parse_cfg_attr<'c, S: Stage>(
const CFG_ATTR_TEMPLATE: AttributeTemplate = template!(
List: &["predicate, attr1, attr2, ..."],
"https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute"
);
pub fn parse_cfg<'c, S: Stage>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> Option<CfgEntry> {
@ -70,9 +84,7 @@ pub(crate) fn parse_cfg_entry<S: Stage>(
},
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
let Some(name) = meta.path().word_sym() else {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: meta.path().span(),
});
cx.expected_identifier(meta.path().span());
return None;
};
parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)?
@ -81,7 +93,7 @@ pub(crate) fn parse_cfg_entry<S: Stage>(
MetaItemOrLitParser::Lit(lit) => match lit.kind {
LitKind::Bool(b) => CfgEntry::Bool(b, lit.span),
_ => {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: lit.span });
cx.expected_identifier(lit.span);
return None;
}
},
@ -149,9 +161,7 @@ fn parse_cfg_entry_target<S: Stage>(
// Then, parse it as a name-value item
let Some(name) = sub_item.path().word_sym() else {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: sub_item.path().span(),
});
cx.expected_identifier(sub_item.path().span());
return None;
};
let name = Symbol::intern(&format!("target_{name}"));
@ -300,3 +310,122 @@ impl EvalConfigResult {
}
}
}
pub fn parse_cfg_attr(
cfg_attr: &Attribute,
sess: &Session,
features: Option<&Features>,
) -> Option<(CfgEntry, Vec<(AttrItem, Span)>)> {
match cfg_attr.get_normal_item().args {
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
if !tokens.is_empty() =>
{
check_cfg_attr_bad_delim(&sess.psess, dspan, delim);
match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| {
parse_cfg_attr_internal(p, sess, features, cfg_attr)
}) {
Ok(r) => return Some(r),
Err(e) => {
let suggestions =
CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr);
e.with_span_suggestions(
cfg_attr.span,
"must be of the form",
suggestions,
Applicability::HasPlaceholders,
)
.with_note(format!(
"for more information, visit <{}>",
CFG_ATTR_TEMPLATE.docs.expect("cfg_attr has docs")
))
.emit();
}
}
}
_ => {
let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) =
cfg_attr.get_normal_item().args
{
(dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument)
} else {
(cfg_attr.span, AttributeParseErrorReason::ExpectedList)
};
sess.dcx().emit_err(AttributeParseError {
span,
attr_span: cfg_attr.span,
template: CFG_ATTR_TEMPLATE,
attribute: AttrPath::from_ast(&cfg_attr.get_normal_item().path),
reason,
suggestions: CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr),
});
}
}
None
}
fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
if let Delimiter::Parenthesis = delim {
return;
}
psess.dcx().emit_err(CfgAttrBadDelim {
span: span.entire(),
sugg: MetaBadDelimSugg { open: span.open, close: span.close },
});
}
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
fn parse_cfg_attr_internal<'a>(
parser: &mut Parser<'a>,
sess: &'a Session,
features: Option<&Features>,
attribute: &Attribute,
) -> PResult<'a, (CfgEntry, Vec<(ast::AttrItem, Span)>)> {
// Parse cfg predicate
let pred_start = parser.token.span;
let meta = MetaItemOrLitParser::parse_single(parser, ShouldEmit::ErrorsAndLints)?;
let pred_span = pred_start.with_hi(parser.token.span.hi());
let cfg_predicate = AttributeParser::parse_single_args(
sess,
attribute.span,
attribute.get_normal_item().span(),
attribute.style,
AttrPath {
segments: attribute
.ident_path()
.expect("cfg_attr is not a doc comment")
.into_boxed_slice(),
span: attribute.span,
},
pred_span,
CRATE_NODE_ID,
features,
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&CFG_ATTR_TEMPLATE,
)
.ok_or_else(|| {
let mut diag = sess.dcx().struct_err(
"cfg_entry parsing failing with `ShouldEmit::ErrorsAndLints` should emit a error.",
);
diag.downgrade_to_delayed_bug();
diag
})?;
parser.expect(exp!(Comma))?;
// Presumably, the majority of the time there will only be one attr.
let mut expanded_attrs = Vec::with_capacity(1);
while parser.token != token::Eof {
let lo = parser.token.span;
let item = parser.parse_attr_item(ForceCollect::Yes)?;
expanded_attrs.push((item, lo.to(parser.prev_token.span)));
if !parser.eat(exp!(Comma)) {
break;
}
}
Ok((cfg_predicate, expanded_attrs))
}

View file

@ -20,12 +20,7 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
return None;
};
Some(AttributeKind::CrateName {
name,
name_span: n.value_span,
attr_span: cx.attr_span,
style: cx.attr_style,
})
Some(AttributeKind::CrateName { name, name_span: n.value_span, attr_span: cx.attr_span })
}
}

View file

@ -56,8 +56,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
}
}
ArgParser::NameValue(_) => {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "inline");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;

View file

@ -71,8 +71,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
// Specifically `#[link = "dl"]` is accepted with a FCW
// For more information, see https://github.com/rust-lang/rust/pull/143193
ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
let suggestions = <Self as CombineAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "link");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;

View file

@ -1,4 +1,3 @@
use rustc_ast::AttrStyle;
use rustc_errors::DiagArgValue;
use rustc_hir::attrs::MacroUseArgs;
@ -102,7 +101,7 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
}
}
ArgParser::NameValue(_) => {
let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
let suggestions = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
@ -149,19 +148,14 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let suggestions = || {
<Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(AttrStyle::Inner, "macro_export")
};
let local_inner_macros = match args {
ArgParser::NoArgs => false,
ArgParser::List(list) => {
let Some(l) = list.single() else {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments {
suggestions: suggestions(),
},
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
return None;
@ -170,10 +164,9 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
Some(sym::local_inner_macros) => true,
_ => {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments {
suggestions: suggestions(),
},
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
return None;
@ -182,7 +175,7 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
}
ArgParser::NameValue(_) => {
let span = cx.attr_span;
let suggestions = suggestions();
let suggestions = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(

View file

@ -48,6 +48,7 @@ pub(crate) mod must_use;
pub(crate) mod no_implicit_prelude;
pub(crate) mod non_exhaustive;
pub(crate) mod path;
pub(crate) mod pin_v2;
pub(crate) mod proc_macro_attrs;
pub(crate) mod prototype;
pub(crate) mod repr;
@ -302,7 +303,7 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
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)`,
/// For example, individual representations from `#[repr(...)]` attributes into an `AttributeKind::Repr(x)`,
/// where `x` is a vec of these individual reprs.
const CONVERT: ConvertFn<Self::Item>;

View file

@ -45,8 +45,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
Some(value_str)
}
ArgParser::List(_) => {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "must_use");
let suggestions = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(

View file

@ -0,0 +1,21 @@
use rustc_hir::Target;
use rustc_hir::attrs::AttributeKind;
use rustc_span::{Span, Symbol, sym};
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
use crate::context::Stage;
use crate::target_checking::AllowedTargets;
use crate::target_checking::Policy::Allow;
pub(crate) struct PinV2Parser;
impl<S: Stage> NoArgsAttributeParser<S> for PinV2Parser {
const PATH: &[Symbol] = &[sym::pin_v2];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Enum),
Allow(Target::Struct),
Allow(Target::Union),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PinV2;
}

View file

@ -1,9 +1,18 @@
use super::prelude::*;
use super::util::parse_single_integer;
pub(crate) struct RustcLayoutScalarValidRangeStart;
pub(crate) struct RustcMainParser;
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {
impl<S: Stage> NoArgsAttributeParser<S> for RustcMainParser {
const PATH: &'static [Symbol] = &[sym::rustc_main];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcMain;
}
pub(crate) struct RustcLayoutScalarValidRangeStartParser;
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStartParser {
const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_start];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
@ -16,9 +25,9 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {
}
}
pub(crate) struct RustcLayoutScalarValidRangeEnd;
pub(crate) struct RustcLayoutScalarValidRangeEndParser;
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEnd {
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEndParser {
const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_end];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;

View file

@ -20,8 +20,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
ArgParser::NoArgs => None,
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "ignore");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
@ -32,8 +31,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
Some(str_value)
}
ArgParser::List(_) => {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "ignore");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;

View file

@ -28,7 +28,8 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
}
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
attr.is_doc_comment().is_some()
|| attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
}
pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(

View file

@ -47,13 +47,14 @@ use crate::attributes::must_use::MustUseParser;
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
use crate::attributes::non_exhaustive::NonExhaustiveParser;
use crate::attributes::path::PathParser as PathAttributeParser;
use crate::attributes::pin_v2::PinV2Parser;
use crate::attributes::proc_macro_attrs::{
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
};
use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcMainParser,
RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser,
};
use crate::attributes::semantics::MayDangleParser;
@ -197,8 +198,8 @@ attribute_parsers!(
Single<RecursionLimitParser>,
Single<RustcBuiltinMacroParser>,
Single<RustcForceInlineParser>,
Single<RustcLayoutScalarValidRangeEnd>,
Single<RustcLayoutScalarValidRangeStart>,
Single<RustcLayoutScalarValidRangeEndParser>,
Single<RustcLayoutScalarValidRangeStartParser>,
Single<RustcObjectLifetimeDefaultParser>,
Single<RustcSimdMonomorphizeLaneLimitParser>,
Single<SanitizeParser>,
@ -233,11 +234,13 @@ attribute_parsers!(
Single<WithoutArgs<NonExhaustiveParser>>,
Single<WithoutArgs<ParenSugarParser>>,
Single<WithoutArgs<PassByValueParser>>,
Single<WithoutArgs<PinV2Parser>>,
Single<WithoutArgs<PointeeParser>>,
Single<WithoutArgs<ProcMacroAttributeParser>>,
Single<WithoutArgs<ProcMacroParser>>,
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
Single<WithoutArgs<RustcMainParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<TrackCallerParser>>,
@ -336,8 +339,16 @@ pub struct Late;
/// Gives [`AttributeParser`]s enough information to create errors, for example.
pub struct AcceptContext<'f, 'sess, S: Stage> {
pub(crate) shared: SharedContext<'f, 'sess, S>,
/// The span of the attribute currently being parsed
/// The outer span of the attribute currently being parsed
/// #[attribute(...)]
/// ^^^^^^^^^^^^^^^^^ outer span
/// For attributes in `cfg_attr`, the outer span and inner spans are equal.
pub(crate) attr_span: Span,
/// The inner span of the attribute currently being parsed
/// #[attribute(...)]
/// ^^^^^^^^^^^^^^ inner span
pub(crate) inner_span: Span,
/// Whether it is an inner or outer attribute
pub(crate) attr_style: AttrStyle,
@ -426,7 +437,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -437,7 +448,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -448,7 +459,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedList,
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -459,7 +470,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNoArgs,
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -471,7 +482,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIdentifier,
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -484,7 +495,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNameValue(name),
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -496,7 +507,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::DuplicateKey(key),
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -509,7 +520,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::UnexpectedLiteral,
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -520,7 +531,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSingleArgument,
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -531,7 +542,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -551,7 +562,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: false,
},
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -572,7 +583,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: true,
},
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -592,7 +603,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: true,
list: false,
},
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}
@ -604,6 +615,13 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span,
);
}
pub(crate) fn suggestions(&self) -> Vec<String> {
// If the outer and inner spans are equal, we are parsing an attribute from `cfg_attr`,
// So don't display an attribute style in the suggestions
let style = (self.attr_span != self.inner_span).then_some(self.attr_style);
self.template.suggestions(style, &self.attr_path)
}
}
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {

View file

@ -1,7 +1,7 @@
use std::borrow::Cow;
use rustc_ast as ast;
use rustc_ast::NodeId;
use rustc_ast::{AttrStyle, NodeId};
use rustc_errors::DiagCtxtHandle;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::AttributeKind;
@ -62,7 +62,8 @@ impl<'sess> AttributeParser<'sess, Early> {
)
}
/// Usually you want `parse_limited`, which defaults to no errors.
/// This does the same as `parse_limited`, except it has a `should_emit` parameter which allows it to emit errors.
/// Usually you want `parse_limited`, which emits no errors.
pub fn parse_limited_should_emit(
sess: &'sess Session,
attrs: &[ast::Attribute],
@ -86,6 +87,13 @@ impl<'sess> AttributeParser<'sess, Early> {
parsed.pop()
}
/// This method allows you to parse a list of attributes *before* `rustc_ast_lowering`.
/// This can be used for attributes that would be removed before `rustc_ast_lowering`, such as attributes on macro calls.
///
/// Try to use this as little as possible. Attributes *should* be lowered during
/// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
/// crash if you tried to do so through [`parse_limited_all`](Self::parse_limited_all).
/// Therefore, if `parse_only` is None, then features *must* be provided.
pub fn parse_limited_all(
sess: &'sess Session,
attrs: &[ast::Attribute],
@ -111,6 +119,8 @@ impl<'sess> AttributeParser<'sess, Early> {
)
}
/// This method parses a single attribute, using `parse_fn`.
/// This is useful if you already know what exact attribute this is, and want to parse it.
pub fn parse_single<T>(
sess: &'sess Session,
attr: &ast::Attribute,
@ -121,13 +131,6 @@ impl<'sess> AttributeParser<'sess, Early> {
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option<T>,
template: &AttributeTemplate,
) -> Option<T> {
let mut parser = Self {
features,
tools: Vec::new(),
parse_only: None,
sess,
stage: Early { emit_errors },
};
let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
panic!("parse_single called on a doc attr")
};
@ -136,6 +139,45 @@ impl<'sess> AttributeParser<'sess, Early> {
let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
let path = meta_parser.path();
let args = meta_parser.args();
Self::parse_single_args(
sess,
attr.span,
normal_attr.item.span(),
attr.style,
path.get_attribute_path(),
target_span,
target_node_id,
features,
emit_errors,
args,
parse_fn,
template,
)
}
/// This method is equivalent to `parse_single`, but parses arguments using `parse_fn` using manually created `args`.
/// This is useful when you want to parse other things than attributes using attribute parsers.
pub fn parse_single_args<T, I>(
sess: &'sess Session,
attr_span: Span,
inner_span: Span,
attr_style: AttrStyle,
attr_path: AttrPath,
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
emit_errors: ShouldEmit,
args: &I,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &I) -> Option<T>,
template: &AttributeTemplate,
) -> Option<T> {
let mut parser = Self {
features,
tools: Vec::new(),
parse_only: None,
sess,
stage: Early { emit_errors },
};
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
shared: SharedContext {
cx: &mut parser,
@ -145,10 +187,11 @@ impl<'sess> AttributeParser<'sess, Early> {
crate::lints::emit_attribute_lint(&lint, sess);
},
},
attr_span: attr.span,
attr_style: attr.style,
attr_span,
inner_span,
attr_style,
template,
attr_path: path.get_attribute_path(),
attr_path,
};
parse_fn(&mut cx, args)
}
@ -265,6 +308,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
inner_span: lower_span(attr.get_normal_item().span()),
attr_style: attr.style,
template: &accept.template,
attr_path: path.get_attribute_path(),

View file

@ -105,7 +105,9 @@ mod session_diagnostics;
mod target_checking;
pub mod validate_attr;
pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
pub use attributes::cfg::{
CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr,
};
pub use attributes::cfg_old::*;
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
pub use context::{Early, Late, OmitDoc, ShouldEmit};

View file

@ -8,7 +8,7 @@ use std::fmt::{Debug, Display};
use rustc_ast::token::{self, Delimiter, MetaVarKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
use rustc_ast_pretty::pprust;
use rustc_errors::{Diag, PResult};
use rustc_hir::{self as hir, AttrPath};
@ -124,7 +124,11 @@ impl<'a> ArgParser<'a> {
return None;
}
Self::List(MetaItemListParser::new(args, psess, should_emit)?)
Self::List(
MetaItemListParser::new(&args.tokens, args.dspan.entire(), psess, should_emit)
.map_err(|e| should_emit.emit_err(e))
.ok()?,
)
}
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
eq_span: *eq_span,
@ -186,7 +190,15 @@ pub enum MetaItemOrLitParser<'a> {
Err(Span, ErrorGuaranteed),
}
impl<'a> MetaItemOrLitParser<'a> {
impl<'sess> MetaItemOrLitParser<'sess> {
pub fn parse_single(
parser: &mut Parser<'sess>,
should_emit: ShouldEmit,
) -> PResult<'sess, MetaItemOrLitParser<'static>> {
let mut this = MetaItemListParserContext { parser, should_emit };
this.parse_meta_item_inner()
}
pub fn span(&self) -> Span {
match self {
MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
@ -204,7 +216,7 @@ impl<'a> MetaItemOrLitParser<'a> {
}
}
pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
pub fn meta_item(&self) -> Option<&MetaItemParser<'sess>> {
match self {
MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
_ => None,
@ -542,23 +554,13 @@ pub struct MetaItemListParser<'a> {
}
impl<'a> MetaItemListParser<'a> {
fn new<'sess>(
delim: &'a DelimArgs,
pub(crate) fn new<'sess>(
tokens: &'a TokenStream,
span: Span,
psess: &'sess ParseSess,
should_emit: ShouldEmit,
) -> Option<Self> {
match MetaItemListParserContext::parse(
delim.tokens.clone(),
psess,
delim.dspan.entire(),
should_emit,
) {
Ok(s) => Some(s),
Err(e) => {
should_emit.emit_err(e);
None
}
}
) -> Result<Self, Diag<'sess>> {
MetaItemListParserContext::parse(tokens.clone(), psess, span, should_emit)
}
/// Lets you pick and choose as what you want to parse each element in the list

View file

@ -1,6 +1,6 @@
use std::num::IntErrorKind;
use rustc_ast::{self as ast, AttrStyle, Path};
use rustc_ast::{self as ast, Path};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
@ -613,10 +613,10 @@ pub(crate) enum AttributeParseErrorReason<'a> {
pub(crate) struct AttributeParseError<'a> {
pub(crate) span: Span,
pub(crate) attr_span: Span,
pub(crate) attr_style: AttrStyle,
pub(crate) template: AttributeTemplate,
pub(crate) attribute: AttrPath,
pub(crate) reason: AttributeParseErrorReason<'a>,
pub(crate) suggestions: Vec<String>,
}
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
@ -752,16 +752,15 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
if let Some(link) = self.template.docs {
diag.note(format!("for more information, visit <{link}>"));
}
let suggestions = self.template.suggestions(self.attr_style, &name);
diag.span_suggestions(
self.attr_span,
if suggestions.len() == 1 {
if self.suggestions.len() == 1 {
"must be of the form"
} else {
"try changing it to one of the following valid forms of the attribute"
},
suggestions,
self.suggestions,
Applicability::HasPlaceholders,
);
@ -971,3 +970,12 @@ pub(crate) struct LimitInvalid<'a> {
pub value_span: Span,
pub error_str: &'a str,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_cfg_attr_bad_delim)]
pub(crate) struct CfgAttrBadDelim {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: MetaBadDelimSugg,
}

View file

@ -44,7 +44,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
}
fn apply_early_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
@ -55,7 +55,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
@ -66,7 +66,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
}
fn apply_early_terminator_effect(
&mut self,
&self,
state: &mut Self::Domain,
term: &mir::Terminator<'tcx>,
loc: Location,
@ -77,7 +77,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
term: &'mir mir::Terminator<'tcx>,
loc: Location,
@ -92,7 +92,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
}
fn apply_call_return_effect(
&mut self,
&self,
_state: &mut Self::Domain,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@ -533,7 +533,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
}
fn apply_early_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
@ -542,7 +542,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
location: Location,
@ -590,7 +590,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
}
fn apply_early_terminator_effect(
&mut self,
&self,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@ -599,7 +599,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
_location: Location,

View file

@ -503,8 +503,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
if let hir::Node::Expr(parent_expr) = parent
&& let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
&& let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) =
call_expr.kind
&& let hir::ExprKind::Path(qpath) = call_expr.kind
&& tcx.qpath_is_lang_item(qpath, LangItem::IntoIterIntoIter)
{
// Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
} else if let UseSpans::FnSelfUse { kind: CallKind::Normal { .. }, .. } = move_spans
@ -2312,6 +2312,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let typeck_results = tcx.typeck(self.mir_def_id());
struct ExprFinder<'hir> {
tcx: TyCtxt<'hir>,
issue_span: Span,
expr_span: Span,
body_expr: Option<&'hir hir::Expr<'hir>>,
@ -2336,9 +2337,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
// };
// corresponding to the desugaring of a for loop `for <pat> in <head> { <body> }`.
if let hir::ExprKind::Call(path, [arg]) = ex.kind
&& let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) =
path.kind
&& let hir::ExprKind::Path(qpath) = path.kind
&& self.tcx.qpath_is_lang_item(qpath, LangItem::IntoIterIntoIter)
&& arg.span.contains(self.issue_span)
&& ex.span.desugaring_kind() == Some(DesugaringKind::ForLoop)
{
// Find `IntoIterator::into_iter(<head>)`
self.head = Some(arg);
@ -2355,10 +2357,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
..
}) = stmt.kind
&& let hir::ExprKind::Call(path, _args) = call.kind
&& let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _)) =
path.kind
&& let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind
&& let hir::QPath::LangItem(LangItem::OptionSome, pat_span) = path
&& let hir::ExprKind::Path(qpath) = path.kind
&& self.tcx.qpath_is_lang_item(qpath, LangItem::IteratorNext)
&& let hir::PatKind::Struct(qpath, [field, ..], _) = bind.pat.kind
&& self.tcx.qpath_is_lang_item(qpath, LangItem::OptionSome)
&& call.span.contains(self.issue_span)
{
// Find `<pat>` and the span for the whole `for` loop.
@ -2370,7 +2372,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.loop_bind = Some(ident);
}
self.head_span = Some(*head_span);
self.pat_span = Some(pat_span);
self.pat_span = Some(bind.pat.span);
self.loop_span = Some(stmt.span);
}
@ -2385,6 +2387,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
let mut finder = ExprFinder {
tcx,
expr_span: span,
issue_span,
loop_bind: None,

View file

@ -687,7 +687,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
}
}
Some(Cause::DropVar(local, location)) => {
Some(Cause::DropVar(local, location)) if !is_local_boring(local) => {
let mut should_note_order = false;
if self.local_name(local).is_some()
&& let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place
@ -705,7 +705,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
}
}
Some(Cause::LiveVar(..)) | None => {
Some(Cause::LiveVar(..) | Cause::DropVar(..)) | None => {
// Here, under NLL: no cause was found. Under polonius: no cause was found, or a
// boring local was found, which we ignore like NLLs do to match its diagnostics.
if let Some(region) = self.to_error_region_vid(borrow_region_vid) {

View file

@ -708,8 +708,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
return (false, false, None);
}
let my_def = self.body.source.def_id();
let Some(td) =
tcx.trait_impl_of_assoc(my_def).and_then(|id| self.infcx.tcx.trait_id_of_impl(id))
let Some(td) = tcx.trait_impl_of_assoc(my_def).map(|id| self.infcx.tcx.impl_trait_id(id))
else {
return (false, false, None);
};
@ -1189,7 +1188,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
binding_mode: BindingMode(ByRef::Yes(_), _),
binding_mode: BindingMode(ByRef::Yes(..), _),
..
})) => {
let pattern_span: Span = local_decl.source_info.span;

View file

@ -49,7 +49,7 @@ use rustc_mir_dataflow::move_paths::{
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor, visit_results};
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use smallvec::SmallVec;
@ -119,7 +119,7 @@ pub fn provide(providers: &mut Providers) {
fn mir_borrowck(
tcx: TyCtxt<'_>,
def: LocalDefId,
) -> Result<&DefinitionSiteHiddenTypes<'_>, ErrorGuaranteed> {
) -> Result<&FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'_>>, ErrorGuaranteed> {
assert!(!tcx.is_typeck_child(def.to_def_id()));
let (input_body, _) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
@ -130,7 +130,7 @@ fn mir_borrowck(
Err(guar)
} else if input_body.should_skip() {
debug!("Skipping borrowck because of injected body");
let opaque_types = DefinitionSiteHiddenTypes(Default::default());
let opaque_types = Default::default();
Ok(tcx.arena.alloc(opaque_types))
} else {
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None);
@ -537,13 +537,11 @@ fn borrowck_check_region_constraints<'tcx>(
mbcx.report_region_errors(nll_errors);
}
let (mut flow_analysis, flow_entry_states) =
get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
let flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
visit_results(
body,
traversal::reverse_postorder(body).map(|(bb, _)| bb),
&mut flow_analysis,
&flow_entry_states,
&flow_results,
&mut mbcx,
);
@ -604,7 +602,7 @@ fn get_flow_results<'a, 'tcx>(
move_data: &'a MoveData<'tcx>,
borrow_set: &'a BorrowSet<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
) -> (Borrowck<'a, 'tcx>, Results<BorrowckDomain>) {
) -> Results<'tcx, Borrowck<'a, 'tcx>> {
// We compute these three analyses individually, but them combine them into
// a single results so that `mbcx` can visit them all together.
let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint(
@ -629,14 +627,14 @@ fn get_flow_results<'a, 'tcx>(
ever_inits: ever_inits.analysis,
};
assert_eq!(borrows.results.len(), uninits.results.len());
assert_eq!(borrows.results.len(), ever_inits.results.len());
let results: Results<_> =
itertools::izip!(borrows.results, uninits.results, ever_inits.results)
assert_eq!(borrows.entry_states.len(), uninits.entry_states.len());
assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len());
let entry_states: EntryStates<_> =
itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states)
.map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits })
.collect();
(analysis, results)
Results { analysis, entry_states }
}
pub(crate) struct BorrowckInferCtxt<'tcx> {
@ -790,7 +788,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> {
fn visit_after_early_statement_effect(
&mut self,
_analysis: &mut Borrowck<'a, 'tcx>,
_analysis: &Borrowck<'a, 'tcx>,
state: &BorrowckDomain,
stmt: &Statement<'tcx>,
location: Location,
@ -865,7 +863,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a,
fn visit_after_early_terminator_effect(
&mut self,
_analysis: &mut Borrowck<'a, 'tcx>,
_analysis: &Borrowck<'a, 'tcx>,
state: &BorrowckDomain,
term: &Terminator<'tcx>,
loc: Location,
@ -985,7 +983,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a,
fn visit_after_primary_terminator_effect(
&mut self,
_analysis: &mut Borrowck<'a, 'tcx>,
_analysis: &Borrowck<'a, 'tcx>,
state: &BorrowckDomain,
term: &Terminator<'tcx>,
loc: Location,
@ -2584,6 +2582,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
_ => bug!("Deref of unexpected type: {:?}", base_ty),
}
}
// Check as the inner reference type if it is a field projection
// from the `&pin` pattern
ProjectionElem::Field(FieldIdx::ZERO, _)
if let Some(adt) =
place_base.ty(self.body(), self.infcx.tcx).ty.ty_adt_def()
&& adt.is_pin()
&& self.infcx.tcx.features().pin_ergonomics() =>
{
self.is_mutable(place_base, is_local_mutation_allowed)
}
// All other projections are owned by their base path, so mutable if
// base path is mutable
ProjectionElem::Field(..)

View file

@ -8,11 +8,11 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, OpaqueTypeStorageEntries};
use rustc_infer::traits::ObligationCause;
use rustc_macros::extension;
use rustc_middle::mir::{Body, ConstraintCategory, DefinitionSiteHiddenTypes};
use rustc_middle::mir::{Body, ConstraintCategory};
use rustc_middle::ty::{
self, DefiningScopeKind, EarlyBinder, FallibleTypeFolder, GenericArg, GenericArgsRef,
OpaqueHiddenType, OpaqueTypeKey, Region, RegionVid, Ty, TyCtxt, TypeFoldable,
TypeSuperFoldable, TypeVisitableExt, fold_regions,
self, DefiningScopeKind, DefinitionSiteHiddenType, FallibleTypeFolder, GenericArg,
GenericArgsRef, OpaqueTypeKey, ProvisionalHiddenType, Region, RegionVid, Ty, TyCtxt,
TypeFoldable, TypeSuperFoldable, TypeVisitableExt, fold_regions,
};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_span::Span;
@ -48,7 +48,7 @@ pub(crate) enum DeferredOpaqueTypeError<'tcx> {
/// The opaque type.
opaque_type_key: OpaqueTypeKey<'tcx>,
/// The hidden type containing the member region.
hidden_type: OpaqueHiddenType<'tcx>,
hidden_type: ProvisionalHiddenType<'tcx>,
/// The unexpected region.
member_region: Region<'tcx>,
},
@ -67,7 +67,7 @@ pub(crate) fn clone_and_resolve_opaque_types<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
) -> (OpaqueTypeStorageEntries, Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)>) {
) -> (OpaqueTypeStorageEntries, Vec<(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)>) {
let opaque_types = infcx.clone_opaque_types();
let opaque_types_storage_num_entries = infcx.inner.borrow_mut().opaque_types().num_entries();
let opaque_types = opaque_types
@ -131,37 +131,29 @@ fn nll_var_to_universal_region<'tcx>(
/// and errors if we end up with distinct hidden types.
fn add_hidden_type<'tcx>(
tcx: TyCtxt<'tcx>,
hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>,
hidden_types: &mut FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
def_id: LocalDefId,
hidden_ty: OpaqueHiddenType<'tcx>,
hidden_ty: ty::DefinitionSiteHiddenType<'tcx>,
) {
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
// `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
// only know that once we convert the generic parameters to those of the opaque type.
if let Some(prev) = hidden_types.0.get_mut(&def_id) {
if prev.ty != hidden_ty.ty {
let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| {
let (Ok(e) | Err(e)) = prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit());
e
});
prev.ty = Ty::new_error(tcx, guar);
if let Some(prev) = hidden_types.get_mut(&def_id) {
if prev.ty == hidden_ty.ty {
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
prev.span = prev.span.substitute_dummy(hidden_ty.span);
} else {
let (Ok(guar) | Err(guar)) =
prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit());
*prev = ty::DefinitionSiteHiddenType::new_error(tcx, guar);
}
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
prev.span = prev.span.substitute_dummy(hidden_ty.span);
} else {
hidden_types.0.insert(def_id, hidden_ty);
hidden_types.insert(def_id, hidden_ty);
}
}
fn get_hidden_type<'tcx>(
hidden_types: &DefinitionSiteHiddenTypes<'tcx>,
def_id: LocalDefId,
) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> {
hidden_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty))
}
#[derive(Debug)]
struct DefiningUse<'tcx> {
/// The opaque type using non NLL vars. This uses the actual
@ -169,7 +161,7 @@ struct DefiningUse<'tcx> {
/// to interact with code outside of `rustc_borrowck`.
opaque_type_key: OpaqueTypeKey<'tcx>,
arg_regions: Vec<RegionVid>,
hidden_type: OpaqueHiddenType<'tcx>,
hidden_type: ProvisionalHiddenType<'tcx>,
}
/// This computes the actual hidden types of the opaque types and maps them to their
@ -188,8 +180,8 @@ pub(crate) fn compute_definition_site_hidden_types<'tcx>(
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
constraints: &MirTypeckRegionConstraints<'tcx>,
location_map: Rc<DenseLocationMap>,
hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
hidden_types: &mut FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
opaque_types: &[(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)],
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
let mut errors = Vec::new();
// When computing the hidden type we need to track member constraints.
@ -223,8 +215,8 @@ pub(crate) fn compute_definition_site_hidden_types<'tcx>(
#[instrument(level = "debug", skip_all, ret)]
fn collect_defining_uses<'tcx>(
rcx: &mut RegionCtxt<'_, 'tcx>,
hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
hidden_types: &mut FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
opaque_types: &[(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)],
errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
) -> Vec<DefiningUse<'tcx>> {
let infcx = rcx.infcx;
@ -247,7 +239,7 @@ fn collect_defining_uses<'tcx>(
infcx.tcx,
hidden_types,
opaque_type_key.def_id,
OpaqueHiddenType::new_error(infcx.tcx, guar),
DefinitionSiteHiddenType::new_error(infcx.tcx, guar),
),
_ => debug!(?non_nll_opaque_type_key, ?err, "ignoring non-defining use"),
}
@ -283,7 +275,7 @@ fn collect_defining_uses<'tcx>(
#[instrument(level = "debug", skip(rcx, hidden_types, defining_uses, errors))]
fn compute_definition_site_hidden_types_from_defining_uses<'tcx>(
rcx: &RegionCtxt<'_, 'tcx>,
hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>,
hidden_types: &mut FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
defining_uses: &[DefiningUse<'tcx>],
errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
) {
@ -310,21 +302,20 @@ fn compute_definition_site_hidden_types_from_defining_uses<'tcx>(
hidden_type.span,
"opaque type with non-universal region args",
);
ty::OpaqueHiddenType::new_error(tcx, guar)
ty::ProvisionalHiddenType::new_error(tcx, guar)
}
};
// Now that we mapped the member regions to their final value,
// map the arguments of the opaque type key back to the parameters
// of the opaque type definition.
let ty = infcx
let hidden_type = infcx
.infer_opaque_definition_from_instantiation(opaque_type_key, hidden_type)
.unwrap_or_else(|_| {
Ty::new_error_with_message(
rcx.infcx.tcx,
hidden_type.span,
"deferred invalid opaque type args",
)
let guar = tcx
.dcx()
.span_delayed_bug(hidden_type.span, "deferred invalid opaque type args");
DefinitionSiteHiddenType::new_error(tcx, guar)
});
// Sometimes, when the hidden type is an inference variable, it can happen that
@ -332,7 +323,7 @@ fn compute_definition_site_hidden_types_from_defining_uses<'tcx>(
// usage of the opaque type and we can ignore it. This check is mirrored in typeck's
// writeback.
if !rcx.infcx.tcx.use_typing_mode_borrowck() {
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.skip_binder().kind()
&& alias_ty.def_id == opaque_type_key.def_id.to_def_id()
&& alias_ty.args == opaque_type_key.args
{
@ -364,12 +355,7 @@ fn compute_definition_site_hidden_types_from_defining_uses<'tcx>(
},
));
}
add_hidden_type(
tcx,
hidden_types,
opaque_type_key.def_id,
OpaqueHiddenType { span: hidden_type.span, ty },
);
add_hidden_type(tcx, hidden_types, opaque_type_key.def_id, hidden_type);
}
}
@ -502,13 +488,13 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>(
region_bound_pairs: &RegionBoundPairs<'tcx>,
known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
constraints: &mut MirTypeckRegionConstraints<'tcx>,
hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
hidden_types: &mut FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
opaque_types: &[(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)],
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
let tcx = infcx.tcx;
let mut errors = Vec::new();
for &(key, hidden_type) in opaque_types {
let Some(expected) = get_hidden_type(hidden_types, key.def_id) else {
let Some(expected) = hidden_types.get(&key.def_id) else {
if !tcx.use_typing_mode_borrowck() {
if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
&& alias_ty.def_id == key.def_id.to_def_id()
@ -527,20 +513,26 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>(
hidden_type.span,
"non-defining use in the defining scope with no defining uses",
);
add_hidden_type(tcx, hidden_types, key.def_id, OpaqueHiddenType::new_error(tcx, guar));
add_hidden_type(
tcx,
hidden_types,
key.def_id,
DefinitionSiteHiddenType::new_error(tcx, guar),
);
continue;
};
// We erase all non-member region of the opaque and need to treat these as existentials.
let expected = ty::fold_regions(tcx, expected.instantiate(tcx, key.args), |re, _dbi| {
match re.kind() {
ty::ReErased => infcx.next_nll_region_var(
NllRegionVariableOrigin::Existential { name: None },
|| crate::RegionCtxt::Existential(None),
),
_ => re,
}
});
let expected_ty =
ty::fold_regions(tcx, expected.ty.instantiate(tcx, key.args), |re, _dbi| {
match re.kind() {
ty::ReErased => infcx.next_nll_region_var(
NllRegionVariableOrigin::Existential { name: None },
|| crate::RegionCtxt::Existential(None),
),
_ => re,
}
});
// We now simply equate the expected with the actual hidden type.
let locations = Locations::All(hidden_type.span);
@ -561,13 +553,18 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>(
);
// We need to normalize both types in the old solver before equatingt them.
let actual_ty = ocx.normalize(&cause, infcx.param_env, hidden_type.ty);
let expected_ty = ocx.normalize(&cause, infcx.param_env, expected.ty);
let expected_ty = ocx.normalize(&cause, infcx.param_env, expected_ty);
ocx.eq(&cause, infcx.param_env, actual_ty, expected_ty).map_err(|_| NoSolution)
},
"equating opaque types",
),
) {
add_hidden_type(tcx, hidden_types, key.def_id, OpaqueHiddenType::new_error(tcx, guar));
add_hidden_type(
tcx,
hidden_types,
key.def_id,
DefinitionSiteHiddenType::new_error(tcx, guar),
);
}
}
errors
@ -682,8 +679,8 @@ impl<'tcx> InferCtxt<'tcx> {
fn infer_opaque_definition_from_instantiation(
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
instantiated_ty: OpaqueHiddenType<'tcx>,
) -> Result<Ty<'tcx>, NonDefiningUseReason<'tcx>> {
instantiated_ty: ProvisionalHiddenType<'tcx>,
) -> Result<ty::DefinitionSiteHiddenType<'tcx>, NonDefiningUseReason<'tcx>> {
opaque_type_has_defining_use_args(
self,
opaque_type_key,
@ -691,15 +688,12 @@ impl<'tcx> InferCtxt<'tcx> {
DefiningScopeKind::MirBorrowck,
)?;
let definition_ty = instantiated_ty
.remap_generic_params_to_declaration_params(
opaque_type_key,
self.tcx,
DefiningScopeKind::MirBorrowck,
)
.ty;
definition_ty.error_reported()?;
let definition_ty = instantiated_ty.remap_generic_params_to_declaration_params(
opaque_type_key,
self.tcx,
DefiningScopeKind::MirBorrowck,
);
definition_ty.ty.skip_binder().error_reported()?;
Ok(definition_ty)
}
}

View file

@ -21,10 +21,10 @@ pub(crate) fn renumber_mir<'tcx>(
let mut renumberer = RegionRenumberer { infcx };
for body in promoted.iter_mut() {
renumberer.visit_body(body);
renumberer.visit_body_preserves_cfg(body);
}
renumberer.visit_body(body);
renumberer.visit_body_preserves_cfg(body);
}
// The fields are used only for debugging output in `sccs_info`.

View file

@ -17,9 +17,8 @@ use crate::region_infer::opaque_types::{
};
use crate::type_check::{Locations, constraint_conversion};
use crate::{
ClosureRegionRequirements, CollectRegionConstraintsResult, DefinitionSiteHiddenTypes,
PropagatedBorrowCheckResults, borrowck_check_region_constraints,
borrowck_collect_region_constraints,
ClosureRegionRequirements, CollectRegionConstraintsResult, PropagatedBorrowCheckResults,
borrowck_check_region_constraints, borrowck_collect_region_constraints,
};
/// The shared context used by both the root as well as all its nested
@ -27,7 +26,7 @@ use crate::{
pub(super) struct BorrowCheckRootCtxt<'tcx> {
pub tcx: TyCtxt<'tcx>,
root_def_id: LocalDefId,
hidden_types: DefinitionSiteHiddenTypes<'tcx>,
hidden_types: FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
/// The region constraints computed by [borrowck_collect_region_constraints]. This uses
/// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before
/// their parents.
@ -72,7 +71,10 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
&self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars
}
pub(super) fn finalize(self) -> Result<&'tcx DefinitionSiteHiddenTypes<'tcx>, ErrorGuaranteed> {
pub(super) fn finalize(
self,
) -> Result<&'tcx FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>, ErrorGuaranteed>
{
if let Some(guar) = self.tainted_by_errors {
Err(guar)
} else {

View file

@ -1046,16 +1046,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
&Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, ty) => {
let trait_ref =
ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, span), [ty]);
self.prove_trait_ref(
trait_ref,
location.to_locations(),
ConstraintCategory::SizedBound,
);
}
&Rvalue::NullaryOp(NullOp::ContractChecks, _) => {}
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}

View file

@ -283,7 +283,7 @@ builtin_macros_requires_cfg_pattern =
macro requires a cfg-pattern as an argument
.label = cfg-pattern required
builtin_macros_source_uitls_expected_item = expected item, found `{$token}`
builtin_macros_source_utils_expected_item = expected item, found `{$token}`
builtin_macros_takes_no_arguments = {$name} takes no arguments

View file

@ -1,3 +1,4 @@
use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, global_fn_name};
use rustc_ast::{
self as ast, Fn, FnHeader, FnSig, Generics, ItemKind, Safety, Stmt, StmtKind, TyKind,
};
@ -55,7 +56,7 @@ pub(crate) fn expand(
}
// #[rustc_std_internal_symbol]
// unsafe fn __rg_oom(size: usize, align: usize) -> ! {
// unsafe fn __rust_alloc_error_handler(size: usize, align: usize) -> ! {
// handler(core::alloc::Layout::from_size_align_unchecked(size, align))
// }
fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span) -> Stmt {
@ -84,7 +85,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
let kind = ItemKind::Fn(Box::new(Fn {
defaultness: ast::Defaultness::Final,
sig,
ident: Ident::from_str_and_span("__rg_oom", span),
ident: Ident::from_str_and_span(&global_fn_name(ALLOC_ERROR_HANDLER), span),
generics: Generics::default(),
contract: None,
body,

View file

@ -17,7 +17,7 @@ impl AttrProcMacro for ExpandRequires {
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
expand_requires_tts(ecx, span, annotation, annotated)
expand_contract_clause_tts(ecx, span, annotation, annotated, kw::ContractRequires)
}
}
@ -29,7 +29,7 @@ impl AttrProcMacro for ExpandEnsures {
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
expand_ensures_tts(ecx, span, annotation, annotated)
expand_contract_clause_tts(ecx, span, annotation, annotated, kw::ContractEnsures)
}
}
@ -71,6 +71,14 @@ fn expand_contract_clause(
.span_err(attr_span, "contract annotations can only be used on functions"));
}
// Contracts are not yet supported on async/gen functions
if new_tts.iter().any(|tt| is_kw(tt, kw::Async) || is_kw(tt, kw::Gen)) {
return Err(ecx.sess.dcx().span_err(
attr_span,
"contract annotations are not yet supported on async or gen functions",
));
}
// Found the `fn` keyword, now find either the `where` token or the function body.
let next_tt = loop {
let Some(tt) = cursor.next() else {
@ -122,48 +130,23 @@ fn expand_contract_clause(
Ok(new_tts)
}
fn expand_requires_tts(
fn expand_contract_clause_tts(
ecx: &mut ExtCtxt<'_>,
attr_span: Span,
annotation: TokenStream,
annotated: TokenStream,
clause_keyword: rustc_span::Symbol,
) -> Result<TokenStream, ErrorGuaranteed> {
let feature_span = ecx.with_def_site_ctxt(attr_span);
expand_contract_clause(ecx, attr_span, annotated, |new_tts| {
new_tts.push_tree(TokenTree::Token(
token::Token::from_ast_ident(Ident::new(kw::ContractRequires, feature_span)),
Spacing::Joint,
));
new_tts.push_tree(TokenTree::Token(
token::Token::new(token::TokenKind::OrOr, attr_span),
Spacing::Alone,
));
new_tts.push_tree(TokenTree::Delimited(
DelimSpan::from_single(attr_span),
DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden),
token::Delimiter::Parenthesis,
annotation,
));
Ok(())
})
}
fn expand_ensures_tts(
ecx: &mut ExtCtxt<'_>,
attr_span: Span,
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
let feature_span = ecx.with_def_site_ctxt(attr_span);
expand_contract_clause(ecx, attr_span, annotated, |new_tts| {
new_tts.push_tree(TokenTree::Token(
token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, feature_span)),
token::Token::from_ast_ident(Ident::new(clause_keyword, feature_span)),
Spacing::Joint,
));
new_tts.push_tree(TokenTree::Delimited(
DelimSpan::from_single(attr_span),
DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden),
token::Delimiter::Parenthesis,
token::Delimiter::Brace,
annotation,
));
Ok(())

View file

@ -610,7 +610,7 @@ impl<'a> TraitDef<'a> {
defaultness: ast::Defaultness::Final,
ident,
generics: Generics::default(),
where_clauses: ast::TyAliasWhereClauses::default(),
after_where_clause: ast::WhereClause::default(),
bounds: Vec::new(),
ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)),
})),
@ -1507,7 +1507,7 @@ impl<'a> TraitDef<'a> {
struct_def: &'a VariantData,
prefixes: &[String],
by_ref: ByRef,
) -> ThinVec<Box<ast::Pat>> {
) -> ThinVec<ast::Pat> {
prefixes
.iter()
.map(|prefix| {
@ -1543,7 +1543,7 @@ impl<'a> TraitDef<'a> {
attrs: ast::AttrVec::new(),
id: ast::DUMMY_NODE_ID,
span: pat.span.with_ctxt(self.span.ctxt()),
pat,
pat: Box::new(pat),
is_placeholder: false,
}
})

View file

@ -952,7 +952,7 @@ pub(crate) struct AttributeOnlyUsableWithCrateType<'a> {
}
#[derive(Diagnostic)]
#[diag(builtin_macros_source_uitls_expected_item)]
#[diag(builtin_macros_source_utils_expected_item)]
pub(crate) struct ExpectedItem<'a> {
#[primary_span]
pub span: Span,

View file

@ -151,7 +151,7 @@ impl AllocFnFactory<'_, '_> {
self.cx.expr_ident(self.span, ident)
}
AllocatorTy::ResultPtr | AllocatorTy::Unit => {
AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => {
panic!("can't convert AllocatorTy to an argument")
}
}
@ -163,7 +163,7 @@ impl AllocFnFactory<'_, '_> {
AllocatorTy::Unit => self.cx.ty(self.span, TyKind::Tup(ThinVec::new())),
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
AllocatorTy::Layout | AllocatorTy::Never | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("can't convert `AllocatorTy` to an output")
}
}

View file

@ -30,28 +30,34 @@ fn parse_pat_ty<'a>(
let ty = parser.parse_ty()?;
parser.expect_keyword(exp!(Is))?;
let pat = pat_to_ty_pat(
cx,
*parser.parse_pat_no_top_guard(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)?,
);
let start = parser.token.span;
let pat = if parser.eat(exp!(Bang)) {
parser.expect_keyword(exp!(Null))?;
ty_pat(TyPatKind::NotNull, start.to(parser.token.span))
} else {
pat_to_ty_pat(
cx,
parser.parse_pat_no_top_guard(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)?,
)
};
if parser.token != token::Eof {
parser.unexpected()?;
}
Ok((ty, pat))
Ok((ty, Box::new(pat)))
}
fn ty_pat(kind: TyPatKind, span: Span) -> Box<TyPat> {
Box::new(TyPat { id: DUMMY_NODE_ID, kind, span, tokens: None })
fn ty_pat(kind: TyPatKind, span: Span) -> TyPat {
TyPat { id: DUMMY_NODE_ID, kind, span, tokens: None }
}
fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> Box<TyPat> {
fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> TyPat {
let kind = match pat.kind {
ast::PatKind::Range(start, end, include_end) => TyPatKind::Range(
start.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })),
@ -59,7 +65,7 @@ fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> Box<TyPat> {
include_end,
),
ast::PatKind::Or(variants) => {
TyPatKind::Or(variants.into_iter().map(|pat| pat_to_ty_pat(cx, *pat)).collect())
TyPatKind::Or(variants.into_iter().map(|pat| pat_to_ty_pat(cx, pat)).collect())
}
ast::PatKind::Err(guar) => TyPatKind::Err(guar),
_ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")),

View file

@ -3,6 +3,7 @@
use std::mem;
use rustc_ast as ast;
use rustc_ast::attr::contains_name;
use rustc_ast::entry::EntryPointType;
use rustc_ast::mut_visit::*;
use rustc_ast::visit::Visitor;
@ -172,9 +173,11 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> {
fn entry_point_type(item: &ast::Item, at_root: bool) -> EntryPointType {
match &item.kind {
ast::ItemKind::Fn(fn_) => {
rustc_ast::entry::entry_point_type(&item.attrs, at_root, Some(fn_.ident.name))
}
ast::ItemKind::Fn(fn_) => rustc_ast::entry::entry_point_type(
contains_name(&item.attrs, sym::rustc_main),
at_root,
Some(fn_.ident.name),
),
_ => EntryPointType::None,
}
}

View file

@ -72,10 +72,6 @@ pub fn debug_tuple() -> DebugTuple {
DebugTuple(())
}
pub fn size_of<T>() -> usize {
intrinsics::size_of::<T>()
}
pub fn use_size_of() -> usize {
size_of::<u64>()
}

View file

@ -6,6 +6,7 @@
extern_types,
decl_macro,
rustc_attrs,
rustc_private,
transparent_unions,
auto_traits,
freeze_impls,
@ -594,7 +595,7 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
impl<T> Box<T> {
pub fn new(val: T) -> Box<T> {
unsafe {
let size = intrinsics::size_of::<T>();
let size = size_of::<T>();
let ptr = libc::malloc(size);
intrinsics::copy(&val as *const T as *const u8, ptr, size);
Box(Unique { pointer: NonNull(ptr as *const T), _marker: PhantomData }, Global)
@ -646,11 +647,11 @@ pub mod intrinsics {
#[rustc_intrinsic]
pub fn abort() -> !;
#[rustc_intrinsic]
pub fn size_of<T>() -> usize;
pub const fn size_of<T>() -> usize;
#[rustc_intrinsic]
pub unsafe fn size_of_val<T: ?::Sized>(val: *const T) -> usize;
#[rustc_intrinsic]
pub fn align_of<T>() -> usize;
pub const fn align_of<T>() -> usize;
#[rustc_intrinsic]
pub unsafe fn align_of_val<T: ?::Sized>(val: *const T) -> usize;
#[rustc_intrinsic]
@ -715,6 +716,23 @@ impl<T> Index<usize> for [T] {
}
}
pub const fn size_of<T>() -> usize {
<T as SizedTypeProperties>::SIZE
}
pub const fn align_of<T>() -> usize {
<T as SizedTypeProperties>::ALIGN
}
trait SizedTypeProperties: Sized {
#[lang = "mem_size_const"]
const SIZE: usize = intrinsics::size_of::<Self>();
#[lang = "mem_align_const"]
const ALIGN: usize = intrinsics::align_of::<Self>();
}
impl<T> SizedTypeProperties for T {}
extern "C" {
type VaListImpl;
}

View file

@ -109,10 +109,10 @@ fn start<T: Termination + 'static>(
puts(*argv as *const i8);
}
unsafe {
puts(*((argv as usize + intrinsics::size_of::<*const u8>()) as *const *const i8));
puts(*((argv as usize + size_of::<*const u8>()) as *const *const i8));
}
unsafe {
puts(*((argv as usize + 2 * intrinsics::size_of::<*const u8>()) as *const *const i8));
puts(*((argv as usize + 2 * size_of::<*const u8>()) as *const *const i8));
}
}
@ -213,8 +213,8 @@ fn main() {
assert_eq!(intrinsics::size_of_val(a) as u8, 16);
assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4);
assert_eq!(intrinsics::align_of::<u16>() as u8, 2);
assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8);
assert_eq!(align_of::<u16>() as u8, 2);
assert_eq!(intrinsics::align_of_val(&a) as u8, align_of::<&str>() as u8);
let u8_needs_drop = const { intrinsics::needs_drop::<u8>() };
assert!(!u8_needs_drop);

View file

@ -467,7 +467,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
true
} else {
instance.is_some_and(|inst| {
fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD)
fx.tcx.codegen_instance_attrs(inst.def).flags.contains(CodegenFnAttrFlags::COLD)
})
};
if is_cold {

View file

@ -3,10 +3,9 @@
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,
AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name,
};
use rustc_codegen_ssa::base::allocator_kind_for_codegen;
use rustc_codegen_ssa::base::{allocator_kind_for_codegen, allocator_shim_contents};
use rustc_session::config::OomStrategy;
use rustc_symbol_mangling::mangle_internal_symbol;
@ -15,74 +14,56 @@ use crate::prelude::*;
/// Returns whether an allocator shim was created
pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool {
let Some(kind) = allocator_kind_for_codegen(tcx) else { return false };
codegen_inner(
tcx,
module,
kind,
tcx.alloc_error_handler_kind(()).unwrap(),
tcx.sess.opts.unstable_opts.oom,
);
let methods = allocator_shim_contents(tcx, kind);
codegen_inner(tcx, module, &methods, tcx.sess.opts.unstable_opts.oom);
true
}
fn codegen_inner(
tcx: TyCtxt<'_>,
module: &mut dyn Module,
kind: AllocatorKind,
alloc_error_handler_kind: AllocatorKind,
methods: &[AllocatorMethod],
oom_strategy: OomStrategy,
) {
let usize_ty = module.target_config().pointer_type();
if kind == AllocatorKind::Default {
for method in ALLOCATOR_METHODS {
let mut arg_tys = Vec::with_capacity(method.inputs.len());
for input in method.inputs.iter() {
match input.ty {
AllocatorTy::Layout => {
arg_tys.push(usize_ty); // size
arg_tys.push(usize_ty); // align
}
AllocatorTy::Ptr => arg_tys.push(usize_ty),
AllocatorTy::Usize => arg_tys.push(usize_ty),
for method in methods {
let mut arg_tys = Vec::with_capacity(method.inputs.len());
for input in method.inputs.iter() {
match input.ty {
AllocatorTy::Layout => {
arg_tys.push(usize_ty); // size
arg_tys.push(usize_ty); // align
}
AllocatorTy::Ptr => arg_tys.push(usize_ty),
AllocatorTy::Usize => arg_tys.push(usize_ty),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => {
panic!("invalid allocator arg")
}
}
let output = match method.output {
AllocatorTy::ResultPtr => Some(usize_ty),
AllocatorTy::Unit => None,
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output")
}
};
let sig = Signature {
call_conv: module.target_config().default_call_conv,
params: arg_tys.iter().cloned().map(AbiParam::new).collect(),
returns: output.into_iter().map(AbiParam::new).collect(),
};
crate::common::create_wrapper_function(
module,
sig,
&mangle_internal_symbol(tcx, &global_fn_name(method.name)),
&mangle_internal_symbol(tcx, &default_fn_name(method.name)),
);
}
}
let output = match method.output {
AllocatorTy::ResultPtr => Some(usize_ty),
AllocatorTy::Never | AllocatorTy::Unit => None,
let sig = Signature {
call_conv: module.target_config().default_call_conv,
params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)],
returns: vec![],
};
crate::common::create_wrapper_function(
module,
sig,
&mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)),
);
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output")
}
};
let sig = Signature {
call_conv: module.target_config().default_call_conv,
params: arg_tys.iter().cloned().map(AbiParam::new).collect(),
returns: output.into_iter().map(AbiParam::new).collect(),
};
crate::common::create_wrapper_function(
module,
sig,
&mangle_internal_symbol(tcx, &global_fn_name(method.name)),
&mangle_internal_symbol(tcx, &default_fn_name(method.name)),
);
}
{
let sig = Signature {

View file

@ -829,19 +829,10 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
fx.bcx.ins().nop();
}
}
Rvalue::ShallowInitBox(ref operand, content_ty) => {
let content_ty = fx.monomorphize(content_ty);
let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty));
let operand = codegen_operand(fx, operand);
let operand = operand.load_scalar(fx);
lval.write_cvalue(fx, CValue::by_val(operand, box_layout));
}
Rvalue::NullaryOp(ref null_op, ty) => {
assert!(lval.layout().ty.is_sized(fx.tcx, fx.typing_env()));
let layout = fx.layout_of(fx.monomorphize(ty));
let val = match null_op {
NullOp::SizeOf => layout.size.bytes(),
NullOp::AlignOf => layout.align.bytes(),
NullOp::OffsetOf(fields) => fx
.tcx
.offset_of_subfield(
@ -924,6 +915,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
lval.write_cvalue_transmute(fx, operand);
}
Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"),
Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"),
}
}
StatementKind::StorageLive(_)

View file

@ -5,7 +5,9 @@ use std::cmp::Ordering;
use cranelift_module::*;
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::interpret::{AllocId, GlobalAlloc, Scalar, read_target_uint};
use rustc_middle::mir::interpret::{
AllocId, GlobalAlloc, PointerArithmetic, Scalar, read_target_uint,
};
use rustc_middle::ty::{ExistentialTraitRef, ScalarInt};
use crate::prelude::*;
@ -138,8 +140,11 @@ pub(crate) fn codegen_const_value<'tcx>(
let base_addr = match fx.tcx.global_alloc(alloc_id) {
GlobalAlloc::Memory(alloc) => {
if alloc.inner().len() == 0 {
assert_eq!(offset, Size::ZERO);
fx.bcx.ins().iconst(fx.pointer_type, alloc.inner().align.bytes() as i64)
let val = alloc.inner().align.bytes().wrapping_add(offset.bytes());
fx.bcx.ins().iconst(
fx.pointer_type,
fx.tcx.truncate_to_target_usize(val) as i64,
)
} else {
let data_id = data_id_for_alloc_id(
&mut fx.constants_cx,

View file

@ -671,18 +671,7 @@ pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box<OngoingCodegen> {
}
.to_owned();
let cgus = if tcx.sess.opts.output_types.should_codegen() {
tcx.collect_and_partition_mono_items(()).codegen_units
} else {
// If only `--emit metadata` is used, we shouldn't perform any codegen.
// Also `tcx.collect_and_partition_mono_items` may panic in that case.
return Box::new(OngoingCodegen {
modules: vec![],
allocator_module: None,
crate_info: CrateInfo::new(tcx, target_cpu),
concurrency_limiter: ConcurrencyLimiter::new(0),
});
};
let cgus = tcx.collect_and_partition_mono_items(()).codegen_units;
if tcx.dep_graph.is_fully_enabled() {
for cgu in cgus {

View file

@ -33,9 +33,7 @@ fn create_jit_module(tcx: TyCtxt<'_>) -> (UnwindModule<JITModule>, CodegenCx) {
}
pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec<String>) -> ! {
if !tcx.sess.opts.output_types.should_codegen() {
tcx.dcx().fatal("JIT mode doesn't work with `cargo check`");
}
// FIXME error on check mode or crate types other than bin in CodegenBackend::init()
if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) {
tcx.dcx().fatal("can't jit non-executable crate");

View file

@ -26,13 +26,12 @@ extern crate rustc_fs_util;
extern crate rustc_hir;
extern crate rustc_incremental;
extern crate rustc_index;
extern crate rustc_log;
extern crate rustc_metadata;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_symbol_mangling;
extern crate rustc_target;
#[macro_use]
extern crate tracing;
// This prevents duplicating functions and statics that are already part of the host rustc process.
#[allow(unused_extern_crates)]
@ -46,6 +45,7 @@ use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::settings::{self, Configurable};
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_codegen_ssa::{CodegenResults, TargetConfig};
use rustc_log::tracing::info;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_session::Session;
use rustc_session::config::OutputFilenames;

View file

@ -131,6 +131,11 @@ pub(crate) fn coerce_unsized_into<'tcx>(
dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout()));
};
match (&src_ty.kind(), &dst_ty.kind()) {
(ty::Pat(a, _), ty::Pat(b, _)) => {
let src = src.cast_pat_ty_to_base(fx.layout_of(*a));
let dst = dst.place_transmute_type(fx, *b);
return coerce_unsized_into(fx, src, dst);
}
(&ty::Ref(..), &ty::Ref(..))
| (&ty::Ref(..), &ty::RawPtr(..))
| (&ty::RawPtr(..), &ty::RawPtr(..)) => coerce_ptr(),

View file

@ -342,6 +342,14 @@ impl<'tcx> CValue<'tcx> {
assert_eq!(self.layout().backend_repr, layout.backend_repr);
CValue(self.0, layout)
}
pub(crate) fn cast_pat_ty_to_base(self, layout: TyAndLayout<'tcx>) -> Self {
let ty::Pat(base, _) = *self.layout().ty.kind() else {
panic!("not a pattern type: {:#?}", self.layout())
};
assert_eq!(layout.ty, base);
CValue(self.0, layout)
}
}
/// A place where you can write a value to or read a value from

View file

@ -2,8 +2,7 @@
use gccjit::FnAttribute;
use gccjit::{Context, FunctionType, RValue, ToRValue, Type};
use rustc_ast::expand::allocator::{
ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
alloc_error_handler_name, default_fn_name, global_fn_name,
AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name,
};
use rustc_middle::bug;
use rustc_middle::ty::TyCtxt;
@ -18,8 +17,7 @@ pub(crate) unsafe fn codegen(
tcx: TyCtxt<'_>,
mods: &mut GccContext,
_module_name: &str,
kind: AllocatorKind,
alloc_error_handler_kind: AllocatorKind,
methods: &[AllocatorMethod],
) {
let context = &mods.context;
let usize = match tcx.sess.target.pointer_width {
@ -31,45 +29,35 @@ pub(crate) unsafe fn codegen(
let i8 = context.new_type::<i8>();
let i8p = i8.make_pointer();
if kind == AllocatorKind::Default {
for method in ALLOCATOR_METHODS {
let mut types = Vec::with_capacity(method.inputs.len());
for input in method.inputs.iter() {
match input.ty {
AllocatorTy::Layout => {
types.push(usize);
types.push(usize);
}
AllocatorTy::Ptr => types.push(i8p),
AllocatorTy::Usize => types.push(usize),
for method in methods {
let mut types = Vec::with_capacity(method.inputs.len());
for input in method.inputs.iter() {
match input.ty {
AllocatorTy::Layout => {
types.push(usize);
types.push(usize);
}
AllocatorTy::Ptr => types.push(i8p),
AllocatorTy::Usize => types.push(usize),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => {
panic!("invalid allocator arg")
}
}
let output = match method.output {
AllocatorTy::ResultPtr => Some(i8p),
AllocatorTy::Unit => None,
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output")
}
};
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, Some(&to_name), &types, output);
}
}
let output = match method.output {
AllocatorTy::ResultPtr => Some(i8p),
AllocatorTy::Never | AllocatorTy::Unit => None,
// FIXME(bjorn3): Add noreturn attribute
create_wrapper_function(
tcx,
context,
&mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))),
&[usize, usize],
None,
);
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output")
}
};
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, Some(&to_name), &types, output);
}
create_const_value_function(
tcx,

View file

@ -546,9 +546,16 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
}
if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
// TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient
// on all architectures. For instance, what about FP stack?
extended_asm.add_clobber("cc");
match asm_arch {
InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {
// "cc" is cr0 on powerpc.
}
// TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient
// on all architectures. For instance, what about FP stack?
_ => {
extended_asm.add_clobber("cc");
}
}
}
if !options.contains(InlineAsmOptions::NOMEM) {
extended_asm.add_clobber("memory");
@ -698,6 +705,7 @@ fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str {
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b",
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f",
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v",
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vsreg) => "wa",
InlineAsmRegClass::PowerPC(
PowerPCInlineAsmRegClass::cr
| PowerPCInlineAsmRegClass::ctr
@ -778,9 +786,9 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => {
cx.type_vector(cx.type_i32(), 4)
}
InlineAsmRegClass::PowerPC(
PowerPCInlineAsmRegClass::vreg | PowerPCInlineAsmRegClass::vsreg,
) => cx.type_vector(cx.type_i32(), 4),
InlineAsmRegClass::PowerPC(
PowerPCInlineAsmRegClass::cr
| PowerPCInlineAsmRegClass::ctr
@ -957,6 +965,13 @@ fn modifier_to_gcc(
InlineAsmRegClass::LoongArch(_) => None,
InlineAsmRegClass::Mips(_) => None,
InlineAsmRegClass::Nvptx(_) => None,
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vsreg) => {
if modifier.is_none() {
Some('x')
} else {
modifier
}
}
InlineAsmRegClass::PowerPC(_) => None,
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg)
| InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None,

View file

@ -30,6 +30,7 @@ use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
use rustc_data_structures::memmap::Mmap;
use rustc_errors::DiagCtxtHandle;
use rustc_log::tracing::info;
use rustc_middle::bug;
use rustc_middle::dep_graph::WorkProduct;
use rustc_session::config::Lto;

View file

@ -5,6 +5,7 @@ use rustc_codegen_ssa::back::link::ensure_removed;
use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
use rustc_fs_util::link_or_copy;
use rustc_log::tracing::debug;
use rustc_session::config::OutputType;
use rustc_target::spec::SplitDebuginfo;

View file

@ -5,7 +5,7 @@ use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods,
};
use rustc_middle::mir::Mutability;
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, PointerArithmetic, Scalar};
use rustc_middle::ty::layout::LayoutOf;
use crate::context::CodegenCx;
@ -247,8 +247,8 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
// 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());
let val = alloc.inner().align.bytes().wrapping_add(offset.bytes());
let val = self.const_usize(self.tcx.truncate_to_target_usize(val));
return if matches!(layout.primitive(), Pointer(_)) {
self.context.new_cast(None, val, ty)
} else {

View file

@ -8,6 +8,7 @@ use rustc_codegen_ssa::traits::{
use rustc_hir::attrs::Linkage;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_log::tracing::trace;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::interpret::{
self, ConstAllocation, ErrorHandled, Scalar as InterpScalar, read_target_uint,

View file

@ -1,8 +1,8 @@
#[cfg(feature = "master")]
use gccjit::Context;
use rustc_codegen_ssa::target_features;
use rustc_data_structures::smallvec::{SmallVec, smallvec};
use rustc_session::Session;
use smallvec::{SmallVec, smallvec};
fn gcc_features_by_flags(sess: &Session, features: &mut Vec<String>) {
target_features::retpoline_features_by_flags(sess, features);

View file

@ -1,7 +1,5 @@
/*
* TODO(antoyo): implement equality in libgccjit based on https://zpz.github.io/blog/overloading-equality-operator-in-cpp-class-hierarchy/ (for type equality?)
* 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.
@ -25,12 +23,6 @@
#![deny(clippy::pattern_type_mismatch)]
#![allow(clippy::needless_lifetimes, clippy::uninlined_format_args)]
// These crates are pulled from the sysroot because they are part of
// rustc's public API, so we need to ensure version compatibility.
extern crate smallvec;
#[macro_use]
extern crate tracing;
// The rustc crates we need
extern crate rustc_abi;
extern crate rustc_apfloat;
@ -44,6 +36,7 @@ extern crate rustc_hir;
extern crate rustc_index;
#[cfg(feature = "master")]
extern crate rustc_interface;
extern crate rustc_log;
extern crate rustc_macros;
extern crate rustc_middle;
extern crate rustc_session;
@ -92,7 +85,7 @@ use back::lto::{ThinBuffer, ThinData};
use gccjit::{CType, Context, OptimizationLevel};
#[cfg(feature = "master")]
use gccjit::{TargetInfo, Version};
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_ast::expand::allocator::AllocatorMethod;
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule};
use rustc_codegen_ssa::back::write::{
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn,
@ -284,8 +277,7 @@ impl ExtraBackendMethods for GccCodegenBackend {
&self,
tcx: TyCtxt<'_>,
module_name: &str,
kind: AllocatorKind,
alloc_error_handler_kind: AllocatorKind,
methods: &[AllocatorMethod],
) -> Self::Module {
let mut mods = GccContext {
context: Arc::new(SyncContext::new(new_context(tcx))),
@ -295,7 +287,7 @@ impl ExtraBackendMethods for GccCodegenBackend {
};
unsafe {
allocator::codegen(tcx, &mut mods, module_name, kind, alloc_error_handler_kind);
allocator::codegen(tcx, &mut mods, module_name, methods);
}
mods
}

View file

@ -39,13 +39,11 @@ trait ArgAttributesExt {
const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] =
[(ArgAttribute::InReg, llvm::AttributeKind::InReg)];
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 4] = [
(ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias),
(ArgAttribute::CapturesAddress, llvm::AttributeKind::CapturesAddress),
(ArgAttribute::NonNull, llvm::AttributeKind::NonNull),
(ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly),
(ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef),
(ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly),
];
fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> {
@ -81,15 +79,23 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
}
for (attr, llattr) in OPTIMIZATION_ATTRIBUTES {
if regular.contains(attr) {
// captures(...) is only available since LLVM 21.
if (attr == ArgAttribute::CapturesReadOnly || attr == ArgAttribute::CapturesAddress)
&& llvm_util::get_version() < (21, 0, 0)
{
continue;
}
attrs.push(llattr.create_attr(cx.llcx));
}
}
// captures(...) is only available since LLVM 21.
if (21, 0, 0) <= llvm_util::get_version() {
const CAPTURES_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 3] = [
(ArgAttribute::CapturesNone, llvm::AttributeKind::CapturesNone),
(ArgAttribute::CapturesAddress, llvm::AttributeKind::CapturesAddress),
(ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly),
];
for (attr, llattr) in CAPTURES_ATTRIBUTES {
if regular.contains(attr) {
attrs.push(llattr.create_attr(cx.llcx));
break;
}
}
}
} else if cx.tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) {
// If we're not optimising, *but* memory sanitizer is on, emit noundef, since it affects
// memory sanitizer's behavior.

View file

@ -1,14 +1,13 @@
use libc::c_uint;
use rustc_ast::expand::allocator::{
ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
alloc_error_handler_name, default_fn_name, global_fn_name,
AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, SpecialAllocatorMethod,
default_fn_name, global_fn_name,
};
use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _;
use rustc_middle::bug;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{DebugInfo, OomStrategy};
use rustc_span::sym;
use rustc_symbol_mangling::mangle_internal_symbol;
use crate::attributes::llfn_attrs_from_instance;
@ -21,8 +20,7 @@ pub(crate) unsafe fn codegen(
tcx: TyCtxt<'_>,
cx: SimpleCx<'_>,
module_name: &str,
kind: AllocatorKind,
alloc_error_handler_kind: AllocatorKind,
methods: &[AllocatorMethod],
) {
let usize = match tcx.sess.target.pointer_width {
16 => cx.type_i16(),
@ -33,91 +31,85 @@ pub(crate) unsafe fn codegen(
let i8 = cx.type_i8();
let i8p = cx.type_ptr();
if kind == AllocatorKind::Default {
for method in ALLOCATOR_METHODS {
let mut args = Vec::with_capacity(method.inputs.len());
for input in method.inputs.iter() {
match input.ty {
AllocatorTy::Layout => {
args.push(usize); // size
args.push(usize); // align
}
AllocatorTy::Ptr => args.push(i8p),
AllocatorTy::Usize => args.push(usize),
for method in methods {
let mut args = Vec::with_capacity(method.inputs.len());
for input in method.inputs.iter() {
match input.ty {
AllocatorTy::Layout => {
args.push(usize); // size
args.push(usize); // align
}
AllocatorTy::Ptr => args.push(i8p),
AllocatorTy::Usize => args.push(usize),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => {
panic!("invalid allocator arg")
}
}
let output = match method.output {
AllocatorTy::ResultPtr => Some(i8p),
AllocatorTy::Unit => None,
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output")
}
};
let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name));
let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name));
let alloc_attr_flag = match method.name {
sym::alloc => CodegenFnAttrFlags::ALLOCATOR,
sym::dealloc => CodegenFnAttrFlags::DEALLOCATOR,
sym::realloc => CodegenFnAttrFlags::REALLOCATOR,
sym::alloc_zeroed => CodegenFnAttrFlags::ALLOCATOR_ZEROED,
_ => unreachable!("Unknown allocator method!"),
};
let mut attrs = CodegenFnAttrs::new();
attrs.flags |= alloc_attr_flag;
create_wrapper_function(
tcx,
&cx,
&from_name,
Some(&to_name),
&args,
output,
false,
&attrs,
);
}
}
// rust alloc error handler
create_wrapper_function(
tcx,
&cx,
&mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))),
&[usize, usize], // size, align
None,
true,
&CodegenFnAttrs::new(),
);
let mut no_return = false;
let output = match method.output {
AllocatorTy::ResultPtr => Some(i8p),
AllocatorTy::Unit => None,
AllocatorTy::Never => {
no_return = true;
None
}
unsafe {
// __rust_alloc_error_handler_should_panic_v2
create_const_value_function(
tcx,
&cx,
&mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
&i8,
&llvm::LLVMConstInt(i8, tcx.sess.opts.unstable_opts.oom.should_panic() as u64, FALSE),
);
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output")
}
};
// __rust_no_alloc_shim_is_unstable_v2
let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name));
let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name));
let alloc_attr_flag = match method.special {
Some(SpecialAllocatorMethod::Alloc) => CodegenFnAttrFlags::ALLOCATOR,
Some(SpecialAllocatorMethod::Dealloc) => CodegenFnAttrFlags::DEALLOCATOR,
Some(SpecialAllocatorMethod::Realloc) => CodegenFnAttrFlags::REALLOCATOR,
Some(SpecialAllocatorMethod::AllocZeroed) => CodegenFnAttrFlags::ALLOCATOR_ZEROED,
None => CodegenFnAttrFlags::empty(),
};
let mut attrs = CodegenFnAttrs::new();
attrs.flags |= alloc_attr_flag;
create_wrapper_function(
tcx,
&cx,
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
None,
&[],
None,
false,
&CodegenFnAttrs::new(),
&from_name,
Some(&to_name),
&args,
output,
no_return,
&attrs,
);
}
// __rust_alloc_error_handler_should_panic_v2
create_const_value_function(
tcx,
&cx,
&mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
&i8,
unsafe {
llvm::LLVMConstInt(i8, tcx.sess.opts.unstable_opts.oom.should_panic() as u64, FALSE)
},
);
// __rust_no_alloc_shim_is_unstable_v2
create_wrapper_function(
tcx,
&cx,
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
None,
&[],
None,
false,
&CodegenFnAttrs::new(),
);
if tcx.sess.opts.debuginfo != DebugInfo::None {
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod);
debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);

View file

@ -658,6 +658,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b",
PowerPC(PowerPCInlineAsmRegClass::freg) => "f",
PowerPC(PowerPCInlineAsmRegClass::vreg) => "v",
PowerPC(PowerPCInlineAsmRegClass::vsreg) => "^wa",
PowerPC(
PowerPCInlineAsmRegClass::cr
| PowerPCInlineAsmRegClass::ctr
@ -748,6 +749,12 @@ fn modifier_to_llvm(
LoongArch(_) => None,
Mips(_) => None,
Nvptx(_) => None,
PowerPC(PowerPCInlineAsmRegClass::vsreg) => {
// The documentation for the 'x' modifier is missing for llvm, and the gcc
// documentation is simply "use this for any vsx argument". It is needed
// to ensure the correct vsx register number is used.
if modifier.is_none() { Some('x') } else { modifier }
}
PowerPC(_) => None,
RiscV(RiscVInlineAsmRegClass::reg) | RiscV(RiscVInlineAsmRegClass::freg) => None,
RiscV(RiscVInlineAsmRegClass::vreg) => unreachable!("clobber-only"),
@ -831,6 +838,7 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
PowerPC(PowerPCInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i32(), 4),
PowerPC(PowerPCInlineAsmRegClass::vsreg) => cx.type_vector(cx.type_i32(), 4),
PowerPC(
PowerPCInlineAsmRegClass::cr
| PowerPCInlineAsmRegClass::ctr
@ -1061,9 +1069,10 @@ fn llvm_fixup_input<'ll, 'tcx>(
let value = bx.or(value, bx.const_u32(0xFFFF_0000));
bx.bitcast(value, bx.type_f32())
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F32) =>
{
(
PowerPC(PowerPCInlineAsmRegClass::vreg | PowerPCInlineAsmRegClass::vsreg),
BackendRepr::Scalar(s),
) if s.primitive() == Primitive::Float(Float::F32) => {
let value = bx.insert_element(
bx.const_undef(bx.type_vector(bx.type_f32(), 4)),
value,
@ -1071,9 +1080,10 @@ fn llvm_fixup_input<'ll, 'tcx>(
);
bx.bitcast(value, bx.type_vector(bx.type_f32(), 4))
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F64) =>
{
(
PowerPC(PowerPCInlineAsmRegClass::vreg | PowerPCInlineAsmRegClass::vsreg),
BackendRepr::Scalar(s),
) if s.primitive() == Primitive::Float(Float::F64) => {
let value = bx.insert_element(
bx.const_undef(bx.type_vector(bx.type_f64(), 2)),
value,
@ -1224,15 +1234,17 @@ fn llvm_fixup_output<'ll, 'tcx>(
let value = bx.trunc(value, bx.type_i16());
bx.bitcast(value, bx.type_f16())
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F32) =>
{
(
PowerPC(PowerPCInlineAsmRegClass::vreg | PowerPCInlineAsmRegClass::vsreg),
BackendRepr::Scalar(s),
) if s.primitive() == Primitive::Float(Float::F32) => {
let value = bx.bitcast(value, bx.type_vector(bx.type_f32(), 4));
bx.extract_element(value, bx.const_usize(0))
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F64) =>
{
(
PowerPC(PowerPCInlineAsmRegClass::vreg | PowerPCInlineAsmRegClass::vsreg),
BackendRepr::Scalar(s),
) if s.primitive() == Primitive::Float(Float::F64) => {
let value = bx.bitcast(value, bx.type_vector(bx.type_f64(), 2));
bx.extract_element(value, bx.const_usize(0))
}
@ -1366,16 +1378,14 @@ fn llvm_fixup_output_type<'ll, 'tcx>(
{
cx.type_f32()
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F32) =>
{
cx.type_vector(cx.type_f32(), 4)
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F64) =>
{
cx.type_vector(cx.type_f64(), 2)
}
(
PowerPC(PowerPCInlineAsmRegClass::vreg | PowerPCInlineAsmRegClass::vsreg),
BackendRepr::Scalar(s),
) if s.primitive() == Primitive::Float(Float::F32) => cx.type_vector(cx.type_f32(), 4),
(
PowerPC(PowerPCInlineAsmRegClass::vreg | PowerPCInlineAsmRegClass::vsreg),
BackendRepr::Scalar(s),
) if s.primitive() == Primitive::Float(Float::F64) => cx.type_vector(cx.type_f64(), 2),
_ => layout.llvm_type(cx),
}
}

View file

@ -1,37 +0,0 @@
#[cfg(test)]
mod tests;
/// Joins command-line arguments into a single space-separated string, quoting
/// and escaping individual arguments as necessary.
///
/// The result is intended to be informational, for embedding in debug metadata,
/// and might not be properly quoted/escaped for actual command-line use.
pub(crate) fn quote_command_line_args(args: &[String]) -> String {
// Start with a decent-sized buffer, since rustc invocations tend to be long.
let mut buf = String::with_capacity(128);
for arg in args {
if !buf.is_empty() {
buf.push(' ');
}
print_arg_quoted(&mut buf, arg);
}
buf
}
/// Equivalent to LLVM's `sys::printArg` with quoting always enabled
/// (see llvm/lib/Support/Program.cpp).
fn print_arg_quoted(buf: &mut String, arg: &str) {
buf.reserve(arg.len() + 2);
buf.push('"');
for ch in arg.chars() {
if matches!(ch, '"' | '\\' | '$') {
buf.push('\\');
}
buf.push(ch);
}
buf.push('"');
}

View file

@ -1,25 +0,0 @@
#[test]
fn quote_command_line_args() {
use super::quote_command_line_args;
struct Case<'a> {
args: &'a [&'a str],
expected: &'a str,
}
let cases = &[
Case { args: &[], expected: "" },
Case { args: &["--hello", "world"], expected: r#""--hello" "world""# },
Case { args: &["--hello world"], expected: r#""--hello world""# },
Case {
args: &["plain", "$dollar", "spa ce", r"back\slash", r#""quote""#, "plain"],
expected: r#""plain" "\$dollar" "spa ce" "back\\slash" "\"quote\"" "plain""#,
},
];
for &Case { args, expected } in cases {
let args = args.iter().copied().map(str::to_owned).collect::<Vec<_>>();
let actual = quote_command_line_args(&args);
assert_eq!(actual, expected, "args {args:?}");
}
}

View file

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

View file

@ -36,10 +36,8 @@ impl OwnedTargetMachine {
use_init_array: bool,
split_dwarf_file: &CStr,
output_obj_file: &CStr,
debug_info_compression: &CStr,
debug_info_compression: llvm::CompressionKind,
use_emulated_tls: bool,
argv0: &str,
command_line_args: &str,
use_wasm_eh: bool,
) -> Result<Self, LlvmError<'static>> {
// SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data
@ -64,12 +62,8 @@ impl OwnedTargetMachine {
use_init_array,
split_dwarf_file.as_ptr(),
output_obj_file.as_ptr(),
debug_info_compression.as_ptr(),
debug_info_compression,
use_emulated_tls,
argv0.as_ptr(),
argv0.len(),
command_line_args.as_ptr(),
command_line_args.len(),
use_wasm_eh,
)
};

View file

@ -6,9 +6,6 @@ use std::sync::Arc;
use std::{fs, slice, str};
use libc::{c_char, c_int, c_void, size_t};
use llvm::{
LLVMRustLLVMHasZlibCompressionForDebugSymbols, LLVMRustLLVMHasZstdCompressionForDebugSymbols,
};
use rustc_codegen_ssa::back::link::ensure_removed;
use rustc_codegen_ssa::back::versioned_llvm_target;
use rustc_codegen_ssa::back::write::{
@ -31,7 +28,6 @@ use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym};
use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel};
use tracing::{debug, trace};
use crate::back::command_line_args::quote_command_line_args;
use crate::back::lto::ThinBuffer;
use crate::back::owned_target_machine::OwnedTargetMachine;
use crate::back::profiling::{
@ -253,34 +249,25 @@ pub(crate) fn target_machine_factory(
let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated);
// Command-line information to be included in the target machine.
// This seems to only be used for embedding in PDB debuginfo files.
// FIXME(Zalathar): Maybe skip this for non-PDB targets?
let argv0 = std::env::current_exe()
.unwrap_or_default()
.into_os_string()
.into_string()
.unwrap_or_default();
let command_line_args = quote_command_line_args(&sess.expanded_args);
// Self-profile counter for the number of bytes produced by command-line quoting.
// Values are summed, so the summary result is cumulative across all TM factories.
sess.prof.artifact_size("quoted_command_line_args", "-", command_line_args.len() as u64);
let debuginfo_compression = sess.opts.debuginfo_compression.to_string();
match sess.opts.debuginfo_compression {
rustc_session::config::DebugInfoCompression::Zlib => {
if !unsafe { LLVMRustLLVMHasZlibCompressionForDebugSymbols() } {
let debuginfo_compression = match sess.opts.debuginfo_compression {
config::DebugInfoCompression::None => llvm::CompressionKind::None,
config::DebugInfoCompression::Zlib => {
if llvm::LLVMRustLLVMHasZlibCompression() {
llvm::CompressionKind::Zlib
} else {
sess.dcx().emit_warn(UnknownCompression { algorithm: "zlib" });
llvm::CompressionKind::None
}
}
rustc_session::config::DebugInfoCompression::Zstd => {
if !unsafe { LLVMRustLLVMHasZstdCompressionForDebugSymbols() } {
config::DebugInfoCompression::Zstd => {
if llvm::LLVMRustLLVMHasZstdCompression() {
llvm::CompressionKind::Zstd
} else {
sess.dcx().emit_warn(UnknownCompression { algorithm: "zstd" });
llvm::CompressionKind::None
}
}
rustc_session::config::DebugInfoCompression::None => {}
};
let debuginfo_compression = SmallCStr::new(&debuginfo_compression);
let file_name_display_preference =
sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO);
@ -324,10 +311,8 @@ pub(crate) fn target_machine_factory(
use_init_array,
&split_dwarf_file,
&output_obj_file,
&debuginfo_compression,
debuginfo_compression,
use_emulated_tls,
&argv0,
&command_line_args,
use_wasm_eh,
)
})
@ -358,7 +343,7 @@ fn write_bitcode_to_file(module: &ModuleCodegen<ModuleLlvm>, path: &Path) {
}
}
/// In what context is a dignostic handler being attached to a codegen unit?
/// In what context is a diagnostic handler being attached to a codegen unit?
pub(crate) enum CodegenDiagnosticsStage {
/// Prelink optimization stage.
Opt,

View file

@ -16,25 +16,42 @@ pub(crate) fn handle_gpu_code<'ll>(
cx: &'ll SimpleCx<'_>,
) {
// The offload memory transfer type for each kernel
let mut o_types = vec![];
let mut kernels = vec![];
let offload_entry_ty = add_tgt_offload_entry(&cx);
let mut memtransfer_types = vec![];
let mut region_ids = vec![];
let offload_entry_ty = TgtOffloadEntry::new_decl(&cx);
for num in 0..9 {
let kernel = cx.get_function(&format!("kernel_{num}"));
if let Some(kernel) = kernel {
o_types.push(gen_define_handling(&cx, kernel, offload_entry_ty, num));
kernels.push(kernel);
let (o, k) = gen_define_handling(&cx, kernel, offload_entry_ty, num);
memtransfer_types.push(o);
region_ids.push(k);
}
}
gen_call_handling(&cx, &kernels, &o_types);
gen_call_handling(&cx, &memtransfer_types, &region_ids);
}
// ; Function Attrs: nounwind
// declare i32 @__tgt_target_kernel(ptr, i64, i32, i32, ptr, ptr) #2
fn generate_launcher<'ll>(cx: &'ll SimpleCx<'_>) -> (&'ll llvm::Value, &'ll llvm::Type) {
let tptr = cx.type_ptr();
let ti64 = cx.type_i64();
let ti32 = cx.type_i32();
let args = vec![tptr, ti64, ti32, ti32, tptr, tptr];
let tgt_fn_ty = cx.type_func(&args, ti32);
let name = "__tgt_target_kernel";
let tgt_decl = declare_offload_fn(&cx, name, tgt_fn_ty);
let nounwind = llvm::AttributeKind::NoUnwind.create_attr(cx.llcx);
attributes::apply_to_llfn(tgt_decl, Function, &[nounwind]);
(tgt_decl, tgt_fn_ty)
}
// What is our @1 here? A magic global, used in our data_{begin/update/end}_mapper:
// @0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
// @1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 22, ptr @0 }, align 8
// FIXME(offload): @0 should include the file name (e.g. lib.rs) in which the function to be
// offloaded was defined.
fn generate_at_one<'ll>(cx: &'ll SimpleCx<'_>) -> &'ll llvm::Value {
// @0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
let unknown_txt = ";unknown;unknown;0;0;;";
let c_entry_name = CString::new(unknown_txt).unwrap();
let c_val = c_entry_name.as_bytes_with_nul();
@ -59,15 +76,7 @@ fn generate_at_one<'ll>(cx: &'ll SimpleCx<'_>) -> &'ll llvm::Value {
at_one
}
pub(crate) fn add_tgt_offload_entry<'ll>(cx: &'ll SimpleCx<'_>) -> &'ll llvm::Type {
let offload_entry_ty = cx.type_named_struct("struct.__tgt_offload_entry");
let tptr = cx.type_ptr();
let ti64 = cx.type_i64();
let ti32 = cx.type_i32();
let ti16 = cx.type_i16();
// For each kernel to run on the gpu, we will later generate one entry of this type.
// copied from LLVM
// typedef struct {
struct TgtOffloadEntry {
// uint64_t Reserved;
// uint16_t Version;
// uint16_t Kind;
@ -77,21 +86,40 @@ pub(crate) fn add_tgt_offload_entry<'ll>(cx: &'ll SimpleCx<'_>) -> &'ll llvm::Ty
// uint64_t Size; Size of the entry info (0 if it is a function)
// uint64_t Data;
// void *AuxAddr;
// } __tgt_offload_entry;
let entry_elements = vec![ti64, ti16, ti16, ti32, tptr, tptr, ti64, ti64, tptr];
cx.set_struct_body(offload_entry_ty, &entry_elements, false);
offload_entry_ty
}
fn gen_tgt_kernel_global<'ll>(cx: &'ll SimpleCx<'_>) {
let kernel_arguments_ty = cx.type_named_struct("struct.__tgt_kernel_arguments");
let tptr = cx.type_ptr();
let ti64 = cx.type_i64();
let ti32 = cx.type_i32();
let tarr = cx.type_array(ti32, 3);
impl TgtOffloadEntry {
pub(crate) fn new_decl<'ll>(cx: &'ll SimpleCx<'_>) -> &'ll llvm::Type {
let offload_entry_ty = cx.type_named_struct("struct.__tgt_offload_entry");
let tptr = cx.type_ptr();
let ti64 = cx.type_i64();
let ti32 = cx.type_i32();
let ti16 = cx.type_i16();
// For each kernel to run on the gpu, we will later generate one entry of this type.
// copied from LLVM
let entry_elements = vec![ti64, ti16, ti16, ti32, tptr, tptr, ti64, ti64, tptr];
cx.set_struct_body(offload_entry_ty, &entry_elements, false);
offload_entry_ty
}
// Taken from the LLVM APITypes.h declaration:
//struct KernelArgsTy {
fn new<'ll>(
cx: &'ll SimpleCx<'_>,
region_id: &'ll Value,
llglobal: &'ll Value,
) -> [&'ll Value; 9] {
let reserved = cx.get_const_i64(0);
let version = cx.get_const_i16(1);
let kind = cx.get_const_i16(1);
let flags = cx.get_const_i32(0);
let size = cx.get_const_i64(0);
let data = cx.get_const_i64(0);
let aux_addr = cx.const_null(cx.type_ptr());
[reserved, version, kind, flags, region_id, llglobal, size, data, aux_addr]
}
}
// Taken from the LLVM APITypes.h declaration:
struct KernelArgsTy {
// uint32_t Version = 0; // Version of this struct for ABI compatibility.
// uint32_t NumArgs = 0; // Number of arguments in each input pointer.
// void **ArgBasePtrs =
@ -102,25 +130,65 @@ fn gen_tgt_kernel_global<'ll>(cx: &'ll SimpleCx<'_>) {
// void **ArgNames = nullptr; // Name of the data for debugging, possibly null.
// void **ArgMappers = nullptr; // User-defined mappers, possibly null.
// uint64_t Tripcount =
// 0; // Tripcount for the teams / distribute loop, 0 otherwise.
// struct {
// 0; // Tripcount for the teams / distribute loop, 0 otherwise.
// struct {
// uint64_t NoWait : 1; // Was this kernel spawned with a `nowait` clause.
// uint64_t IsCUDA : 1; // Was this kernel spawned via CUDA.
// uint64_t Unused : 62;
// } Flags = {0, 0, 0};
// } Flags = {0, 0, 0}; // totals to 64 Bit, 8 Byte
// // The number of teams (for x,y,z dimension).
// uint32_t NumTeams[3] = {0, 0, 0};
// // The number of threads (for x,y,z dimension).
// uint32_t ThreadLimit[3] = {0, 0, 0};
// uint32_t DynCGroupMem = 0; // Amount of dynamic cgroup memory requested.
//};
let kernel_elements =
vec![ti32, ti32, tptr, tptr, tptr, tptr, tptr, tptr, ti64, ti64, tarr, tarr, ti32];
}
cx.set_struct_body(kernel_arguments_ty, &kernel_elements, false);
// For now we don't handle kernels, so for now we just add a global dummy
// to make sure that the __tgt_offload_entry is defined and handled correctly.
cx.declare_global("my_struct_global2", kernel_arguments_ty);
impl KernelArgsTy {
const OFFLOAD_VERSION: u64 = 3;
const FLAGS: u64 = 0;
const TRIPCOUNT: u64 = 0;
fn new_decl<'ll>(cx: &'ll SimpleCx<'_>) -> &'ll Type {
let kernel_arguments_ty = cx.type_named_struct("struct.__tgt_kernel_arguments");
let tptr = cx.type_ptr();
let ti64 = cx.type_i64();
let ti32 = cx.type_i32();
let tarr = cx.type_array(ti32, 3);
let kernel_elements =
vec![ti32, ti32, tptr, tptr, tptr, tptr, tptr, tptr, ti64, ti64, tarr, tarr, ti32];
cx.set_struct_body(kernel_arguments_ty, &kernel_elements, false);
kernel_arguments_ty
}
fn new<'ll>(
cx: &'ll SimpleCx<'_>,
num_args: u64,
memtransfer_types: &[&'ll Value],
geps: [&'ll Value; 3],
) -> [(Align, &'ll Value); 13] {
let four = Align::from_bytes(4).expect("4 Byte alignment should work");
let eight = Align::EIGHT;
let ti32 = cx.type_i32();
let ci32_0 = cx.get_const_i32(0);
[
(four, cx.get_const_i32(KernelArgsTy::OFFLOAD_VERSION)),
(four, cx.get_const_i32(num_args)),
(eight, geps[0]),
(eight, geps[1]),
(eight, geps[2]),
(eight, memtransfer_types[0]),
// The next two are debug infos. FIXME(offload): set them
(eight, cx.const_null(cx.type_ptr())), // dbg
(eight, cx.const_null(cx.type_ptr())), // dbg
(eight, cx.get_const_i64(KernelArgsTy::TRIPCOUNT)),
(eight, cx.get_const_i64(KernelArgsTy::FLAGS)),
(four, cx.const_array(ti32, &[cx.get_const_i32(2097152), ci32_0, ci32_0])),
(four, cx.const_array(ti32, &[cx.get_const_i32(256), ci32_0, ci32_0])),
(four, cx.get_const_i32(0)),
]
}
}
fn gen_tgt_data_mappers<'ll>(
@ -182,12 +250,15 @@ pub(crate) fn add_global<'ll>(
llglobal
}
// This function returns a memtransfer value which encodes how arguments to this kernel shall be
// mapped to/from the gpu. It also returns a region_id with the name of this kernel, to be
// concatenated into the list of region_ids.
fn gen_define_handling<'ll>(
cx: &'ll SimpleCx<'_>,
kernel: &'ll llvm::Value,
offload_entry_ty: &'ll llvm::Type,
num: i64,
) -> &'ll llvm::Value {
) -> (&'ll llvm::Value, &'ll llvm::Value) {
let types = cx.func_params_types(cx.get_type_of_global(kernel));
// It seems like non-pointer values are automatically mapped. So here, we focus on pointer (or
// reference) types.
@ -205,10 +276,14 @@ fn gen_define_handling<'ll>(
// or both to and from the gpu (=3). Other values shouldn't affect us for now.
// A non-mutable reference or pointer will be 1, an array that's not read, but fully overwritten
// will be 2. For now, everything is 3, until we have our frontend set up.
let o_types =
add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{num}"), &vec![3; num_ptr_types]);
// 1+2+32: 1 (MapTo), 2 (MapFrom), 32 (Add one extra input ptr per function, to be used later).
let memtransfer_types = add_priv_unnamed_arr(
&cx,
&format!(".offload_maptypes.{num}"),
&vec![1 + 2 + 32; num_ptr_types],
);
// Next: For each function, generate these three entries. A weak constant,
// the llvm.rodata entry name, and the omp_offloading_entries value
// the llvm.rodata entry name, and the llvm_offload_entries value
let name = format!(".kernel_{num}.region_id");
let initializer = cx.get_const_i8(0);
@ -222,19 +297,10 @@ fn gen_define_handling<'ll>(
let llglobal = add_unnamed_global(&cx, &offload_entry_name, initializer, InternalLinkage);
llvm::set_alignment(llglobal, Align::ONE);
llvm::set_section(llglobal, c".llvm.rodata.offloading");
// Not actively used yet, for calling real kernels
let name = format!(".offloading.entry.kernel_{num}");
// See the __tgt_offload_entry documentation above.
let reserved = cx.get_const_i64(0);
let version = cx.get_const_i16(1);
let kind = cx.get_const_i16(1);
let flags = cx.get_const_i32(0);
let size = cx.get_const_i64(0);
let data = cx.get_const_i64(0);
let aux_addr = cx.const_null(cx.type_ptr());
let elems = vec![reserved, version, kind, flags, region_id, llglobal, size, data, aux_addr];
let elems = TgtOffloadEntry::new(&cx, region_id, llglobal);
let initializer = crate::common::named_struct(offload_entry_ty, &elems);
let c_name = CString::new(name).unwrap();
@ -242,13 +308,13 @@ fn gen_define_handling<'ll>(
llvm::set_global_constant(llglobal, true);
llvm::set_linkage(llglobal, WeakAnyLinkage);
llvm::set_initializer(llglobal, initializer);
llvm::set_alignment(llglobal, Align::ONE);
let c_section_name = CString::new(".omp_offloading_entries").unwrap();
llvm::set_alignment(llglobal, Align::EIGHT);
let c_section_name = CString::new("llvm_offload_entries").unwrap();
llvm::set_section(llglobal, &c_section_name);
o_types
(memtransfer_types, region_id)
}
fn declare_offload_fn<'ll>(
pub(crate) fn declare_offload_fn<'ll>(
cx: &'ll SimpleCx<'_>,
name: &str,
ty: &'ll llvm::Type,
@ -285,9 +351,10 @@ fn declare_offload_fn<'ll>(
// 6. generate __tgt_target_data_end calls to move data from the GPU
fn gen_call_handling<'ll>(
cx: &'ll SimpleCx<'_>,
_kernels: &[&'ll llvm::Value],
o_types: &[&'ll llvm::Value],
memtransfer_types: &[&'ll llvm::Value],
region_ids: &[&'ll llvm::Value],
) {
let (tgt_decl, tgt_target_kernel_ty) = generate_launcher(&cx);
// %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr }
let tptr = cx.type_ptr();
let ti32 = cx.type_i32();
@ -295,7 +362,7 @@ fn gen_call_handling<'ll>(
let tgt_bin_desc = cx.type_named_struct("struct.__tgt_bin_desc");
cx.set_struct_body(tgt_bin_desc, &tgt_bin_desc_ty, false);
gen_tgt_kernel_global(&cx);
let tgt_kernel_decl = KernelArgsTy::new_decl(&cx);
let (begin_mapper_decl, _, end_mapper_decl, fn_ty) = gen_tgt_data_mappers(&cx);
let main_fn = cx.get_function("main");
@ -329,35 +396,32 @@ fn gen_call_handling<'ll>(
// These represent the sizes in bytes, e.g. the entry for `&[f64; 16]` will be 8*16.
let ty2 = cx.type_array(cx.type_i64(), num_args);
let a4 = builder.direct_alloca(ty2, Align::EIGHT, ".offload_sizes");
// Now we allocate once per function param, a copy to be passed to one of our maps.
let mut vals = vec![];
let mut geps = vec![];
let i32_0 = cx.get_const_i32(0);
for (index, in_ty) in types.iter().enumerate() {
// get function arg, store it into the alloca, and read it.
let p = llvm::get_param(called, index as u32);
let name = llvm::get_value_name(p);
let name = str::from_utf8(&name).unwrap();
let arg_name = format!("{name}.addr");
let alloca = builder.direct_alloca(in_ty, Align::EIGHT, &arg_name);
builder.store(p, alloca, Align::EIGHT);
let val = builder.load(in_ty, alloca, Align::EIGHT);
let gep = builder.inbounds_gep(cx.type_f32(), val, &[i32_0]);
vals.push(val);
geps.push(gep);
}
//%kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
let a5 = builder.direct_alloca(tgt_kernel_decl, Align::EIGHT, "kernel_args");
// Step 1)
unsafe { llvm::LLVMRustPositionBefore(builder.llbuilder, kernel_call) };
builder.memset(tgt_bin_desc_alloca, cx.get_const_i8(0), cx.get_const_i64(32), Align::EIGHT);
// Now we allocate once per function param, a copy to be passed to one of our maps.
let mut vals = vec![];
let mut geps = vec![];
let i32_0 = cx.get_const_i32(0);
for index in 0..types.len() {
let v = unsafe { llvm::LLVMGetOperand(kernel_call, index as u32).unwrap() };
let gep = builder.inbounds_gep(cx.type_f32(), v, &[i32_0]);
vals.push(v);
geps.push(gep);
}
let mapper_fn_ty = cx.type_func(&[cx.type_ptr()], cx.type_void());
let register_lib_decl = declare_offload_fn(&cx, "__tgt_register_lib", mapper_fn_ty);
let unregister_lib_decl = declare_offload_fn(&cx, "__tgt_unregister_lib", mapper_fn_ty);
let init_ty = cx.type_func(&[], cx.type_void());
let init_rtls_decl = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty);
// FIXME(offload): Later we want to add them to the wrapper code, rather than our main function.
// call void @__tgt_register_lib(ptr noundef %6)
builder.call(mapper_fn_ty, register_lib_decl, &[tgt_bin_desc_alloca], None);
// call void @__tgt_init_all_rtls()
@ -386,19 +450,19 @@ fn gen_call_handling<'ll>(
a1: &'ll Value,
a2: &'ll Value,
a4: &'ll Value,
) -> (&'ll Value, &'ll Value, &'ll Value) {
) -> [&'ll Value; 3] {
let i32_0 = cx.get_const_i32(0);
let gep1 = builder.inbounds_gep(ty, a1, &[i32_0, i32_0]);
let gep2 = builder.inbounds_gep(ty, a2, &[i32_0, i32_0]);
let gep3 = builder.inbounds_gep(ty2, a4, &[i32_0, i32_0]);
(gep1, gep2, gep3)
[gep1, gep2, gep3]
}
fn generate_mapper_call<'a, 'll>(
builder: &mut SBuilder<'a, 'll>,
cx: &'ll SimpleCx<'ll>,
geps: (&'ll Value, &'ll Value, &'ll Value),
geps: [&'ll Value; 3],
o_type: &'ll Value,
fn_to_call: &'ll Value,
fn_ty: &'ll Type,
@ -409,31 +473,51 @@ fn gen_call_handling<'ll>(
let i64_max = cx.get_const_i64(u64::MAX);
let num_args = cx.get_const_i32(num_args);
let args =
vec![s_ident_t, i64_max, num_args, geps.0, geps.1, geps.2, o_type, nullptr, nullptr];
vec![s_ident_t, i64_max, num_args, geps[0], geps[1], geps[2], o_type, nullptr, nullptr];
builder.call(fn_ty, fn_to_call, &args, None);
}
// Step 2)
let s_ident_t = generate_at_one(&cx);
let o = o_types[0];
let o = memtransfer_types[0];
let geps = get_geps(&mut builder, &cx, ty, ty2, a1, a2, a4);
generate_mapper_call(&mut builder, &cx, geps, o, begin_mapper_decl, fn_ty, num_args, s_ident_t);
let values = KernelArgsTy::new(&cx, num_args, memtransfer_types, geps);
// Step 3)
// Here we will add code for the actual kernel launches in a follow-up PR.
// FIXME(offload): launch kernels
// Here we fill the KernelArgsTy, see the documentation above
for (i, value) in values.iter().enumerate() {
let ptr = builder.inbounds_gep(tgt_kernel_decl, a5, &[i32_0, cx.get_const_i32(i as u64)]);
builder.store(value.1, ptr, value.0);
}
let args = vec![
s_ident_t,
// FIXME(offload) give users a way to select which GPU to use.
cx.get_const_i64(u64::MAX), // MAX == -1.
// FIXME(offload): Don't hardcode the numbers of threads in the future.
cx.get_const_i32(2097152),
cx.get_const_i32(256),
region_ids[0],
a5,
];
let offload_success = builder.call(tgt_target_kernel_ty, tgt_decl, &args, None);
// %41 = call i32 @__tgt_target_kernel(ptr @1, i64 -1, i32 2097152, i32 256, ptr @.kernel_1.region_id, ptr %kernel_args)
unsafe {
let next = llvm::LLVMGetNextInstruction(offload_success).unwrap();
llvm::LLVMRustPositionAfter(builder.llbuilder, next);
llvm::LLVMInstructionEraseFromParent(next);
}
// Step 4)
unsafe { llvm::LLVMRustPositionAfter(builder.llbuilder, kernel_call) };
let geps = get_geps(&mut builder, &cx, ty, ty2, a1, a2, a4);
generate_mapper_call(&mut builder, &cx, geps, o, end_mapper_decl, fn_ty, num_args, s_ident_t);
builder.call(mapper_fn_ty, unregister_lib_decl, &[tgt_bin_desc_alloca], None);
// With this we generated the following begin and end mappers. We could easily generate the
// update mapper in an update.
// call void @__tgt_target_data_begin_mapper(ptr @1, i64 -1, i32 3, ptr %27, ptr %28, ptr %29, ptr @.offload_maptypes, ptr null, ptr null)
// call void @__tgt_target_data_update_mapper(ptr @1, i64 -1, i32 2, ptr %46, ptr %47, ptr %48, ptr @.offload_maptypes.1, ptr null, ptr null)
// call void @__tgt_target_data_end_mapper(ptr @1, i64 -1, i32 3, ptr %49, ptr %50, ptr %51, ptr @.offload_maptypes, ptr null, ptr null)
drop(builder);
// FIXME(offload) The issue is that we right now add a call to the gpu version of the function,
// and then delete the call to the CPU version. In the future, we should use an intrinsic which
// directly resolves to a call to the GPU version.
unsafe { llvm::LLVMDeleteFunction(called) };
}

View file

@ -12,7 +12,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hashes::Hash128;
use rustc_hir::def_id::DefId;
use rustc_middle::bug;
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, PointerArithmetic, Scalar};
use rustc_middle::ty::TyCtxt;
use rustc_session::cstore::DllImport;
use tracing::debug;
@ -281,8 +281,8 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
// 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 llval = self.const_usize(alloc.inner().align.bytes());
let val = alloc.inner().align.bytes().wrapping_add(offset.bytes());
let llval = self.const_usize(self.tcx.truncate_to_target_usize(val));
return if matches!(layout.primitive(), Pointer(_)) {
unsafe { llvm::LLVMConstIntToPtr(llval, llty) }
} else {

View file

@ -128,7 +128,7 @@ impl GlobalFileTable {
for file in all_files {
raw_file_table.entry(file.stable_id).or_insert_with(|| {
file.name
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
.for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE)
.to_string_lossy()
.into_owned()
});
@ -147,7 +147,7 @@ impl GlobalFileTable {
.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
.for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE)
.to_string_lossy();
table.push(base_dir.as_ref());

View file

@ -1325,6 +1325,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
};
}
let llvm_version = crate::llvm_util::get_version();
/// Converts a vector mask, where each element has a bit width equal to the data elements it is used with,
/// down to an i1 based mask that can be used by llvm intrinsics.
///
@ -1808,7 +1810,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
);
// Alignment of T, must be a constant integer value:
let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
let alignment = bx.align_of(in_elem).bytes();
// Truncate the mask vector to a vector of i1s:
let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
@ -1819,11 +1821,23 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
return Ok(bx.call_intrinsic(
"llvm.masked.gather",
&[llvm_elem_vec_ty, llvm_pointer_vec_ty],
&[args[1].immediate(), alignment, mask, args[0].immediate()],
));
let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
let alignment = bx.const_i32(alignment as i32);
&[args[1].immediate(), alignment, mask, args[0].immediate()]
} else {
&[args[1].immediate(), mask, args[0].immediate()]
};
let call =
bx.call_intrinsic("llvm.masked.gather", &[llvm_elem_vec_ty, llvm_pointer_vec_ty], args);
if llvm_version >= (22, 0, 0) {
crate::attributes::apply_to_callsite(
call,
crate::llvm::AttributePlace::Argument(0),
&[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
)
}
return Ok(call);
}
if name == sym::simd_masked_load {
@ -1891,18 +1905,30 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
// Alignment of T, must be a constant integer value:
let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32);
let alignment = bx.align_of(values_elem).bytes();
let llvm_pointer = bx.type_ptr();
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
return Ok(bx.call_intrinsic(
"llvm.masked.load",
&[llvm_elem_vec_ty, llvm_pointer],
&[args[1].immediate(), alignment, mask, args[2].immediate()],
));
let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
let alignment = bx.const_i32(alignment as i32);
&[args[1].immediate(), alignment, mask, args[2].immediate()]
} else {
&[args[1].immediate(), mask, args[2].immediate()]
};
let call = bx.call_intrinsic("llvm.masked.load", &[llvm_elem_vec_ty, llvm_pointer], args);
if llvm_version >= (22, 0, 0) {
crate::attributes::apply_to_callsite(
call,
crate::llvm::AttributePlace::Argument(0),
&[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
)
}
return Ok(call);
}
if name == sym::simd_masked_store {
@ -1964,18 +1990,29 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
// Alignment of T, must be a constant integer value:
let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32);
let alignment = bx.align_of(values_elem).bytes();
let llvm_pointer = bx.type_ptr();
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
return Ok(bx.call_intrinsic(
"llvm.masked.store",
&[llvm_elem_vec_ty, llvm_pointer],
&[args[2].immediate(), args[1].immediate(), alignment, mask],
));
let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
let alignment = bx.const_i32(alignment as i32);
&[args[2].immediate(), args[1].immediate(), alignment, mask]
} else {
&[args[2].immediate(), args[1].immediate(), mask]
};
let call = bx.call_intrinsic("llvm.masked.store", &[llvm_elem_vec_ty, llvm_pointer], args);
if llvm_version >= (22, 0, 0) {
crate::attributes::apply_to_callsite(
call,
crate::llvm::AttributePlace::Argument(1),
&[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
)
}
return Ok(call);
}
if name == sym::simd_scatter {
@ -2040,7 +2077,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
);
// Alignment of T, must be a constant integer value:
let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
let alignment = bx.align_of(in_elem).bytes();
// Truncate the mask vector to a vector of i1s:
let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
@ -2050,12 +2087,25 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
return Ok(bx.call_intrinsic(
let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
let alignment = bx.const_i32(alignment as i32);
&[args[0].immediate(), args[1].immediate(), alignment, mask]
} else {
&[args[0].immediate(), args[1].immediate(), mask]
};
let call = bx.call_intrinsic(
"llvm.masked.scatter",
&[llvm_elem_vec_ty, llvm_pointer_vec_ty],
&[args[0].immediate(), args[1].immediate(), alignment, mask],
));
args,
);
if llvm_version >= (22, 0, 0) {
crate::attributes::apply_to_callsite(
call,
crate::llvm::AttributePlace::Argument(1),
&[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
)
}
return Ok(call);
}
macro_rules! arith_red {

View file

@ -30,7 +30,7 @@ use back::write::{create_informational_target_machine, create_target_machine};
use context::SimpleCx;
use errors::ParseTargetMachineConfig;
use llvm_util::target_config;
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_ast::expand::allocator::AllocatorMethod;
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule};
use rustc_codegen_ssa::back::write::{
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn,
@ -109,14 +109,13 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
&self,
tcx: TyCtxt<'tcx>,
module_name: &str,
kind: AllocatorKind,
alloc_error_handler_kind: AllocatorKind,
methods: &[AllocatorMethod],
) -> ModuleLlvm {
let module_llvm = ModuleLlvm::new_metadata(tcx, module_name);
let cx =
SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size());
unsafe {
allocator::codegen(tcx, cx, module_name, kind, alloc_error_handler_kind);
allocator::codegen(tcx, cx, module_name, methods);
}
module_llvm
}

View file

@ -289,6 +289,7 @@ pub(crate) enum AttributeKind {
DeadOnUnwind = 43,
DeadOnReturn = 44,
CapturesReadOnly = 45,
CapturesNone = 46,
}
/// LLVMIntPredicate
@ -677,6 +678,15 @@ pub(crate) enum Opcode {
CatchSwitch = 65,
}
/// Must match the layout of `LLVMRustCompressionKind`.
#[derive(Copy, Clone)]
#[repr(C)]
pub(crate) enum CompressionKind {
None = 0,
Zlib = 1,
Zstd = 2,
}
unsafe extern "C" {
type Opaque;
}
@ -1117,6 +1127,7 @@ unsafe extern "C" {
// Operations on functions
pub(crate) fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint);
pub(crate) fn LLVMDeleteFunction(Fn: &Value);
// Operations about llvm intrinsics
pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint;
@ -1146,6 +1157,8 @@ unsafe extern "C" {
pub(crate) fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>;
pub(crate) fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock;
pub(crate) fn LLVMGetOperand(Val: &Value, Index: c_uint) -> Option<&Value>;
pub(crate) fn LLVMGetNextInstruction(Val: &Value) -> Option<&Value>;
pub(crate) fn LLVMInstructionEraseFromParent(Val: &Value);
// Operations on call sites
pub(crate) fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint);
@ -2325,12 +2338,8 @@ unsafe extern "C" {
UseInitArray: bool,
SplitDwarfFile: *const c_char,
OutputObjFile: *const c_char,
DebugInfoCompression: *const c_char,
DebugInfoCompression: CompressionKind,
UseEmulatedTls: bool,
Argv0: *const c_uchar, // See "PTR_LEN_STR".
Argv0Len: size_t,
CommandLineArgs: *const c_uchar, // See "PTR_LEN_STR".
CommandLineArgsLen: size_t,
UseWasmEH: bool,
) -> *mut TargetMachine;
@ -2517,9 +2526,8 @@ unsafe extern "C" {
pub(crate) fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32;
pub(crate) fn LLVMRustLLVMHasZlibCompressionForDebugSymbols() -> bool;
pub(crate) fn LLVMRustLLVMHasZstdCompressionForDebugSymbols() -> bool;
pub(crate) safe fn LLVMRustLLVMHasZlibCompression() -> bool;
pub(crate) safe fn LLVMRustLLVMHasZstdCompression() -> bool;
pub(crate) fn LLVMRustGetSymbols(
buf_ptr: *const u8,

View file

@ -213,7 +213,7 @@ impl<'a> IntoIterator for LLVMFeature<'a> {
///
/// Check the current rustc fork of LLVM in the repo at
/// <https://github.com/rust-lang/llvm-project/>. The commit in use can be found via the
/// `llvm-project` submodule in <https://github.com/rust-lang/rust/tree/master/src> Though note that
/// `llvm-project` submodule in <https://github.com/rust-lang/rust/tree/HEAD/src> Though note that
/// Rust can also be build with an external precompiled version of LLVM which might lead to failures
/// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics.
pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFeature<'a>> {

View file

@ -1,7 +1,9 @@
use std::collections::hash_map::Entry::*;
use rustc_abi::{CanonAbi, X86Call};
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name};
use rustc_ast::expand::allocator::{
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name,
};
use rustc_data_structures::unord::UnordMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId};
@ -498,7 +500,7 @@ pub(crate) fn allocator_shim_symbols(
.iter()
.map(move |method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str()))
.chain([
mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
mangle_internal_symbol(tcx, global_fn_name(ALLOC_ERROR_HANDLER).as_str()),
mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
])

View file

@ -15,8 +15,8 @@ use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
use rustc_errors::emitter::Emitter;
use rustc_errors::translation::Translator;
use rustc_errors::{
Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalErrorMarker, Level, MultiSpan, Style,
Suggestions,
Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FatalErrorMarker, Level,
MultiSpan, Style, Suggestions,
};
use rustc_fs_util::link_or_copy;
use rustc_incremental::{
@ -346,12 +346,6 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
pub pointer_size: Size,
/// All commandline args used to invoke the compiler, with @file args fully expanded.
/// This will only be used within debug info, e.g. in the pdb file on windows
/// This is mainly useful for other tools that reads that debuginfo to figure out
/// how to call the compiler with the same arguments.
pub expanded_args: Vec<String>,
/// Emitter to use for diagnostics produced during codegen.
pub diag_emitter: SharedEmitter,
/// LLVM optimizations for which we want to print remarks.
@ -380,7 +374,7 @@ fn generate_thin_lto_work<B: ExtraBackendMethods>(
each_linked_rlib_for_lto: &[PathBuf],
needs_thin_lto: Vec<(String, B::ThinBuffer)>,
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
) -> Vec<(WorkItem<B>, u64)> {
) -> Vec<(ThinLtoWorkItem<B>, u64)> {
let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work");
let (lto_modules, copy_jobs) = B::run_thin_lto(
@ -394,11 +388,11 @@ fn generate_thin_lto_work<B: ExtraBackendMethods>(
.into_iter()
.map(|module| {
let cost = module.cost();
(WorkItem::ThinLto(module), cost)
(ThinLtoWorkItem::ThinLto(module), cost)
})
.chain(copy_jobs.into_iter().map(|wp| {
(
WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen {
ThinLtoWorkItem::CopyPostLtoArtifacts(CachedModuleCodegen {
name: wp.cgu_name.clone(),
source: wp,
}),
@ -703,64 +697,73 @@ pub(crate) enum WorkItem<B: WriteBackendMethods> {
/// Copy the post-LTO artifacts from the incremental cache to the output
/// directory.
CopyPostLtoArtifacts(CachedModuleCodegen),
/// Performs fat LTO on the given module.
FatLto {
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_for_lto: Vec<PathBuf>,
needs_fat_lto: Vec<FatLtoInput<B>>,
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
},
}
enum ThinLtoWorkItem<B: WriteBackendMethods> {
/// Copy the post-LTO artifacts from the incremental cache to the output
/// directory.
CopyPostLtoArtifacts(CachedModuleCodegen),
/// Performs thin-LTO on the given module.
ThinLto(lto::ThinModule<B>),
}
// `pthread_setname()` on *nix ignores anything beyond the first 15
// bytes. Use short descriptions to maximize the space available for
// the module name.
#[cfg(not(windows))]
fn desc(short: &str, _long: &str, name: &str) -> String {
// The short label is three bytes, and is followed by a space. That
// leaves 11 bytes for the CGU name. How we obtain those 11 bytes
// depends on the CGU name form.
//
// - Non-incremental, e.g. `regex.f10ba03eb5ec7975-cgu.0`: the part
// before the `-cgu.0` is the same for every CGU, so use the
// `cgu.0` part. The number suffix will be different for each
// CGU.
//
// - Incremental (normal), e.g. `2i52vvl2hco29us0`: use the whole
// name because each CGU will have a unique ASCII hash, and the
// first 11 bytes will be enough to identify it.
//
// - Incremental (with `-Zhuman-readable-cgu-names`), e.g.
// `regex.f10ba03eb5ec7975-re_builder.volatile`: use the whole
// name. The first 11 bytes won't be enough to uniquely identify
// it, but no obvious substring will, and this is a rarely used
// option so it doesn't matter much.
//
assert_eq!(short.len(), 3);
let name = if let Some(index) = name.find("-cgu.") {
&name[index + 1..] // +1 skips the leading '-'.
} else {
name
};
format!("{short} {name}")
}
// Windows has no thread name length limit, so use more descriptive names.
#[cfg(windows)]
fn desc(_short: &str, long: &str, name: &str) -> String {
format!("{long} {name}")
}
impl<B: WriteBackendMethods> WorkItem<B> {
/// Generate a short description of this work item suitable for use as a thread name.
fn short_description(&self) -> String {
// `pthread_setname()` on *nix ignores anything beyond the first 15
// bytes. Use short descriptions to maximize the space available for
// the module name.
#[cfg(not(windows))]
fn desc(short: &str, _long: &str, name: &str) -> String {
// The short label is three bytes, and is followed by a space. That
// leaves 11 bytes for the CGU name. How we obtain those 11 bytes
// depends on the CGU name form.
//
// - Non-incremental, e.g. `regex.f10ba03eb5ec7975-cgu.0`: the part
// before the `-cgu.0` is the same for every CGU, so use the
// `cgu.0` part. The number suffix will be different for each
// CGU.
//
// - Incremental (normal), e.g. `2i52vvl2hco29us0`: use the whole
// name because each CGU will have a unique ASCII hash, and the
// first 11 bytes will be enough to identify it.
//
// - Incremental (with `-Zhuman-readable-cgu-names`), e.g.
// `regex.f10ba03eb5ec7975-re_builder.volatile`: use the whole
// name. The first 11 bytes won't be enough to uniquely identify
// it, but no obvious substring will, and this is a rarely used
// option so it doesn't matter much.
//
assert_eq!(short.len(), 3);
let name = if let Some(index) = name.find("-cgu.") {
&name[index + 1..] // +1 skips the leading '-'.
} else {
name
};
format!("{short} {name}")
}
// Windows has no thread name length limit, so use more descriptive names.
#[cfg(windows)]
fn desc(_short: &str, long: &str, name: &str) -> String {
format!("{long} {name}")
}
match self {
WorkItem::Optimize(m) => desc("opt", "optimize module", &m.name),
WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for", &m.name),
WorkItem::FatLto { .. } => desc("lto", "fat LTO module", "everything"),
WorkItem::ThinLto(m) => desc("lto", "thin-LTO module", m.name()),
}
}
}
impl<B: WriteBackendMethods> ThinLtoWorkItem<B> {
/// Generate a short description of this work item suitable for use as a thread name.
fn short_description(&self) -> String {
match self {
ThinLtoWorkItem::CopyPostLtoArtifacts(m) => {
desc("cpy", "copy LTO artifacts for", &m.name)
}
ThinLtoWorkItem::ThinLto(m) => desc("lto", "thin-LTO module", m.name()),
}
}
}
@ -891,7 +894,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
module: CachedModuleCodegen,
) -> WorkItemResult<B> {
) -> CompiledModule {
let _timer = cgcx
.prof
.generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*module.name);
@ -964,7 +967,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
cgcx.create_dcx().handle().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name })
}
WorkItemResult::Finished(CompiledModule {
CompiledModule {
links_from_incr_cache,
kind: ModuleKind::Regular,
name: module.name,
@ -973,17 +976,19 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
bytecode,
assembly,
llvm_ir,
})
}
}
fn execute_fat_lto_work_item<B: ExtraBackendMethods>(
fn do_fat_lto<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
mut needs_fat_lto: Vec<FatLtoInput<B>>,
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
) -> WorkItemResult<B> {
let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", "everything");
) -> CompiledModule {
let _timer = cgcx.prof.verbose_generic_activity("LLVM_fatlto");
check_lto_allowed(&cgcx);
for (module, wp) in import_only_modules {
needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module })
@ -995,19 +1000,155 @@ fn execute_fat_lto_work_item<B: ExtraBackendMethods>(
each_linked_rlib_for_lto,
needs_fat_lto,
);
let module = B::codegen(cgcx, module, &cgcx.module_config);
WorkItemResult::Finished(module)
B::codegen(cgcx, module, &cgcx.module_config)
}
fn do_thin_lto<'a, B: ExtraBackendMethods>(
cgcx: &'a CodegenContext<B>,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_for_lto: Vec<PathBuf>,
needs_thin_lto: Vec<(String, <B as WriteBackendMethods>::ThinBuffer)>,
lto_import_only_modules: Vec<(
SerializedModule<<B as WriteBackendMethods>::ModuleBuffer>,
WorkProduct,
)>,
) -> Vec<CompiledModule> {
let _timer = cgcx.prof.verbose_generic_activity("LLVM_thinlto");
check_lto_allowed(&cgcx);
let (coordinator_send, coordinator_receive) = channel();
// First up, convert our jobserver into a helper thread so we can use normal
// mpsc channels to manage our messages and such.
// After we've requested tokens then we'll, when we can,
// get tokens on `coordinator_receive` which will
// get managed in the main loop below.
let coordinator_send2 = coordinator_send.clone();
let helper = jobserver::client()
.into_helper_thread(move |token| {
drop(coordinator_send2.send(ThinLtoMessage::Token(token)));
})
.expect("failed to spawn helper thread");
let mut work_items = vec![];
// We have LTO work to do. Perform the serial work here of
// figuring out what we're going to LTO and then push a
// bunch of work items onto our queue to do LTO. This all
// happens on the coordinator thread but it's very quick so
// we don't worry about tokens.
for (work, cost) in generate_thin_lto_work(
cgcx,
&exported_symbols_for_lto,
&each_linked_rlib_for_lto,
needs_thin_lto,
lto_import_only_modules,
) {
let insertion_index =
work_items.binary_search_by_key(&cost, |&(_, cost)| cost).unwrap_or_else(|e| e);
work_items.insert(insertion_index, (work, cost));
if cgcx.parallel {
helper.request_token();
}
}
let mut codegen_aborted = None;
// These are the Jobserver Tokens we currently hold. Does not include
// the implicit Token the compiler process owns no matter what.
let mut tokens = vec![];
// Amount of tokens that are used (including the implicit token).
let mut used_token_count = 0;
let mut compiled_modules = vec![];
// Run the message loop while there's still anything that needs message
// processing. Note that as soon as codegen is aborted we simply want to
// wait for all existing work to finish, so many of the conditions here
// only apply if codegen hasn't been aborted as they represent pending
// work to be done.
loop {
if codegen_aborted.is_none() {
if used_token_count == 0 && work_items.is_empty() {
// All codegen work is done.
break;
}
// Spin up what work we can, only doing this while we've got available
// parallelism slots and work left to spawn.
while used_token_count < tokens.len() + 1
&& let Some((item, _)) = work_items.pop()
{
spawn_thin_lto_work(&cgcx, coordinator_send.clone(), item);
used_token_count += 1;
}
} else {
// Don't queue up any more work if codegen was aborted, we're
// just waiting for our existing children to finish.
if used_token_count == 0 {
break;
}
}
// Relinquish accidentally acquired extra tokens. Subtract 1 for the implicit token.
tokens.truncate(used_token_count.saturating_sub(1));
match coordinator_receive.recv().unwrap() {
// Save the token locally and the next turn of the loop will use
// this to spawn a new unit of work, or it may get dropped
// immediately if we have no more work to spawn.
ThinLtoMessage::Token(token) => match token {
Ok(token) => {
tokens.push(token);
}
Err(e) => {
let msg = &format!("failed to acquire jobserver token: {e}");
cgcx.diag_emitter.fatal(msg);
codegen_aborted = Some(FatalError);
}
},
ThinLtoMessage::WorkItem { result } => {
// If a thread exits successfully then we drop a token associated
// with that worker and update our `used_token_count` count.
// We may later re-acquire a token to continue running more work.
// We may also not actually drop a token here if the worker was
// running with an "ephemeral token".
used_token_count -= 1;
match result {
Ok(compiled_module) => compiled_modules.push(compiled_module),
Err(Some(WorkerFatalError)) => {
// Like `CodegenAborted`, wait for remaining work to finish.
codegen_aborted = Some(FatalError);
}
Err(None) => {
// If the thread failed that means it panicked, so
// we abort immediately.
bug!("worker thread panicked");
}
}
}
}
}
if let Some(codegen_aborted) = codegen_aborted {
codegen_aborted.raise();
}
compiled_modules
}
fn execute_thin_lto_work_item<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
module: lto::ThinModule<B>,
) -> WorkItemResult<B> {
) -> CompiledModule {
let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", module.name());
let module = B::optimize_thin(cgcx, module);
let module = B::codegen(cgcx, module, &cgcx.module_config);
WorkItemResult::Finished(module)
B::codegen(cgcx, module, &cgcx.module_config)
}
/// Messages sent to the coordinator.
@ -1041,6 +1182,17 @@ pub(crate) enum Message<B: WriteBackendMethods> {
CodegenAborted,
}
/// Messages sent to the coordinator.
pub(crate) enum ThinLtoMessage {
/// A jobserver token has become available. Sent from the jobserver helper
/// thread.
Token(io::Result<Acquired>),
/// The backend has finished processing a work item for a codegen unit.
/// Sent from a backend worker thread.
WorkItem { result: Result<CompiledModule, Option<WorkerFatalError>> },
}
/// A message sent from the coordinator thread to the main thread telling it to
/// process another codegen unit.
pub struct CguMessage;
@ -1092,9 +1244,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
regular_config: Arc<ModuleConfig>,
allocator_config: Arc<ModuleConfig>,
allocator_module: Option<ModuleCodegen<B::Module>>,
tx_to_llvm_workers: Sender<Message<B>>,
coordinator_send: Sender<Message<B>>,
) -> thread::JoinHandle<Result<CompiledModules, ()>> {
let coordinator_send = tx_to_llvm_workers;
let sess = tcx.sess;
let mut each_linked_rlib_for_lto = Vec::new();
@ -1123,13 +1274,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
})
.expect("failed to spawn helper thread");
let ol =
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
// If we know that we wont be doing codegen, create target machines without optimisation.
config::OptLevel::No
} else {
tcx.backend_optimization_level(())
};
let ol = tcx.backend_optimization_level(());
let backend_features = tcx.global_backend_features(());
let remark_dir = if let Some(ref dir) = sess.opts.unstable_opts.remark_dir {
@ -1153,7 +1298,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
remark: sess.opts.cg.remark.clone(),
remark_dir,
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
expanded_args: tcx.sess.expanded_args.clone(),
diag_emitter: shared_emitter.clone(),
output_filenames: Arc::clone(tcx.output_filenames(())),
module_config: regular_config,
@ -1314,7 +1458,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
let mut needs_fat_lto = Vec::new();
let mut needs_thin_lto = Vec::new();
let mut lto_import_only_modules = Vec::new();
let mut started_lto = false;
/// Possible state transitions:
/// - Ongoing -> Completed
@ -1404,63 +1547,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
if running_with_any_token(main_thread_state, running_with_own_token) == 0
&& work_items.is_empty()
{
// All codegen work is done. Do we have LTO work to do?
if needs_fat_lto.is_empty()
&& needs_thin_lto.is_empty()
&& lto_import_only_modules.is_empty()
{
// Nothing more to do!
break;
}
// We have LTO work to do. Perform the serial work here of
// figuring out what we're going to LTO and then push a
// bunch of work items onto our queue to do LTO. This all
// happens on the coordinator thread but it's very quick so
// we don't worry about tokens.
assert!(!started_lto);
started_lto = true;
let needs_fat_lto = mem::take(&mut needs_fat_lto);
let needs_thin_lto = mem::take(&mut needs_thin_lto);
let import_only_modules = mem::take(&mut lto_import_only_modules);
let each_linked_rlib_file_for_lto =
mem::take(&mut each_linked_rlib_file_for_lto);
check_lto_allowed(&cgcx);
if !needs_fat_lto.is_empty() {
assert!(needs_thin_lto.is_empty());
work_items.push((
WorkItem::FatLto {
exported_symbols_for_lto: Arc::clone(&exported_symbols_for_lto),
each_linked_rlib_for_lto: each_linked_rlib_file_for_lto,
needs_fat_lto,
import_only_modules,
},
0,
));
if cgcx.parallel {
helper.request_token();
}
} else {
for (work, cost) in generate_thin_lto_work(
&cgcx,
&exported_symbols_for_lto,
&each_linked_rlib_file_for_lto,
needs_thin_lto,
import_only_modules,
) {
let insertion_index = work_items
.binary_search_by_key(&cost, |&(_, cost)| cost)
.unwrap_or_else(|e| e);
work_items.insert(insertion_index, (work, cost));
if cgcx.parallel {
helper.request_token();
}
}
}
// All codegen work is done.
break;
}
// In this branch, we know that everything has been codegened,
@ -1598,12 +1686,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
compiled_modules.push(compiled_module);
}
Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => {
assert!(!started_lto);
assert!(needs_thin_lto.is_empty());
needs_fat_lto.push(fat_lto_input);
}
Ok(WorkItemResult::NeedsThinLto(name, thin_buffer)) => {
assert!(!started_lto);
assert!(needs_fat_lto.is_empty());
needs_thin_lto.push((name, thin_buffer));
}
@ -1620,7 +1706,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
}
Message::AddImportOnlyModule { module_data, work_product } => {
assert!(!started_lto);
assert_eq!(codegen_state, Ongoing);
assert_eq!(main_thread_state, MainThreadState::Codegenning);
lto_import_only_modules.push((module_data, work_product));
@ -1629,12 +1714,43 @@ fn start_executing_work<B: ExtraBackendMethods>(
}
}
// Drop to print timings
drop(llvm_start_time);
if codegen_state == Aborted {
return Err(());
}
// Drop to print timings
drop(llvm_start_time);
drop(codegen_state);
drop(tokens);
drop(helper);
assert!(work_items.is_empty());
if !needs_fat_lto.is_empty() {
assert!(compiled_modules.is_empty());
assert!(needs_thin_lto.is_empty());
// This uses the implicit token
let module = do_fat_lto(
&cgcx,
&exported_symbols_for_lto,
&each_linked_rlib_file_for_lto,
needs_fat_lto,
lto_import_only_modules,
);
compiled_modules.push(module);
} else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() {
assert!(compiled_modules.is_empty());
assert!(needs_fat_lto.is_empty());
compiled_modules.extend(do_thin_lto(
&cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_thin_lto,
lto_import_only_modules,
));
}
// Regardless of what order these modules completed in, report them to
// the backend in the same order every time to ensure that we're handing
@ -1725,20 +1841,9 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || {
let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work {
WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, m),
WorkItem::CopyPostLtoArtifacts(m) => execute_copy_from_cache_work_item(&cgcx, m),
WorkItem::FatLto {
exported_symbols_for_lto,
each_linked_rlib_for_lto,
needs_fat_lto,
import_only_modules,
} => execute_fat_lto_work_item(
&cgcx,
&exported_symbols_for_lto,
&each_linked_rlib_for_lto,
needs_fat_lto,
import_only_modules,
),
WorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, m),
WorkItem::CopyPostLtoArtifacts(m) => {
WorkItemResult::Finished(execute_copy_from_cache_work_item(&cgcx, m))
}
}));
let msg = match result {
@ -1758,6 +1863,36 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
.expect("failed to spawn work thread");
}
fn spawn_thin_lto_work<'a, B: ExtraBackendMethods>(
cgcx: &'a CodegenContext<B>,
coordinator_send: Sender<ThinLtoMessage>,
work: ThinLtoWorkItem<B>,
) {
let cgcx = cgcx.clone();
B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || {
let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work {
ThinLtoWorkItem::CopyPostLtoArtifacts(m) => execute_copy_from_cache_work_item(&cgcx, m),
ThinLtoWorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, m),
}));
let msg = match result {
Ok(result) => ThinLtoMessage::WorkItem { result: Ok(result) },
// We ignore any `FatalError` coming out of `execute_work_item`, as a
// diagnostic was already sent off to the main thread - just surface
// that there was an error in this worker.
Err(err) if err.is::<FatalErrorMarker>() => {
ThinLtoMessage::WorkItem { result: Err(Some(WorkerFatalError)) }
}
Err(_) => ThinLtoMessage::WorkItem { result: Err(None) },
};
drop(coordinator_send.send(msg));
})
.expect("failed to spawn work thread");
}
enum SharedEmitterMessage {
Diagnostic(Diagnostic),
InlineAsmError(SpanData, String, Level, Option<(String, Vec<InnerSpan>)>),

View file

@ -6,7 +6,9 @@ use std::time::{Duration, Instant};
use itertools::Itertools;
use rustc_abi::FIRST_VARIANT;
use rustc_ast as ast;
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_ast::expand::allocator::{
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorMethod, AllocatorTy,
};
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
use rustc_data_structures::sync::{IntoDynSyncSend, par_map};
@ -226,6 +228,7 @@ pub(crate) fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
) -> (Bx::Value, Bx::Value) {
debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
match (src_ty.kind(), dst_ty.kind()) {
(&ty::Pat(a, _), &ty::Pat(b, _)) => unsize_ptr(bx, src, a, b, old_info),
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _))
| (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => {
assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());
@ -655,22 +658,32 @@ pub(crate) fn needs_allocator_shim_for_linking(
!any_dynamic_crate
}
pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec<AllocatorMethod> {
let mut methods = Vec::new();
if kind == AllocatorKind::Default {
methods.extend(ALLOCATOR_METHODS.into_iter().copied());
}
// If the return value of allocator_kind_for_codegen is Some then
// alloc_error_handler_kind must also be Some.
if tcx.alloc_error_handler_kind(()).unwrap() == AllocatorKind::Default {
methods.push(AllocatorMethod {
name: ALLOC_ERROR_HANDLER,
special: None,
inputs: &[],
output: AllocatorTy::Never,
});
}
methods
}
pub fn codegen_crate<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'_>,
target_cpu: String,
) -> OngoingCodegen<B> {
// Skip crate items and just output metadata in -Z no-codegen mode.
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, None);
ongoing_codegen.codegen_finished(tcx);
ongoing_codegen.check_for_errors(tcx.sess);
return ongoing_codegen;
}
if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() {
// The target has no default cpu, but none is set explicitly
tcx.dcx().emit_fatal(errors::CpuRequired);
@ -699,14 +712,8 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
tcx.sess.time("write_allocator_module", || {
let module = backend.codegen_allocator(
tcx,
&llmod_id,
kind,
// If allocator_kind is Some then alloc_error_handler_kind must
// also be Some.
tcx.alloc_error_handler_kind(()).unwrap(),
);
let module =
backend.codegen_allocator(tcx, &llmod_id, &allocator_shim_contents(tcx, kind));
Some(ModuleCodegen::new_allocator(llmod_id, module))
})
} else {

View file

@ -279,7 +279,7 @@ fn process_builtin_attrs(
AttributeKind::StdInternalSymbol(_) => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
}
AttributeKind::Linkage(linkage, _) => {
AttributeKind::Linkage(linkage, span) => {
let linkage = Some(*linkage);
if tcx.is_foreign_item(did) {
@ -287,7 +287,7 @@ fn process_builtin_attrs(
if tcx.is_mutable_static(did.into()) {
let mut diag = tcx.dcx().struct_span_err(
attr.span(),
*span,
"extern mutable statics are not allowed with `#[linkage]`",
);
diag.note(

View file

@ -200,10 +200,11 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
let fn_ty = bx.fn_decl_backend_type(fn_abi);
let fn_attrs = if bx.tcx().def_kind(fx.instance.def_id()).has_codegen_attrs() {
Some(bx.tcx().codegen_fn_attrs(fx.instance.def_id()))
Some(bx.tcx().codegen_instance_attrs(fx.instance.def))
} else {
None
};
let fn_attrs = fn_attrs.as_deref();
if !fn_abi.can_unwind {
unwind = mir::UnwindAction::Unreachable;

View file

@ -120,8 +120,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
| sym::atomic_singlethreadfence
| sym::caller_location => {}
_ => {
span_bug!(span, "nullary intrinsic {name} must either be in a const block or explicitly opted out because it is inherently a runtime intrinsic
");
span_bug!(
span,
"Nullary intrinsic {name} must be called in a const block. \
If you are seeing this message from code outside the standard library, the \
unstable implementation details of the relevant intrinsic may have changed. \
Consider using stable APIs instead. \
If you are adding a new nullary intrinsic that is inherently a runtime \
intrinsic, update this check."
);
}
}
}

View file

@ -40,12 +40,12 @@ impl<'tcx, V> Locals<'tcx, V> {
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
pub(super) fn initialize_locals(&mut self, values: Vec<LocalRef<'tcx, Bx::Value>>) {
assert!(self.locals.values.is_empty());
self.locals.values = IndexVec::from_raw(values);
// FIXME(#115215): After #115025 get's merged this might not be necessary
for (local, value) in values.into_iter().enumerate() {
for (local, value) in self.locals.values.iter_enumerated() {
match value {
LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (),
LocalRef::Operand(op) => {
let local = mir::Local::from_usize(local);
let expected_ty = self.monomorphize(self.mir.local_decls[local].ty);
if expected_ty != op.layout.ty {
warn!(
@ -56,7 +56,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
}
self.locals.values.push(value);
}
}

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