Merge pull request #20960 from rust-lang/rustc-pull
minor: Rustc pull update
This commit is contained in:
commit
5873327cd4
3648 changed files with 55330 additions and 26590 deletions
2
.github/ISSUE_TEMPLATE/tracking_issue.md
vendored
2
.github/ISSUE_TEMPLATE/tracking_issue.md
vendored
|
|
@ -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
|
||||
<!--
|
||||
|
|
|
|||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/post-merge.yml
vendored
2
.github/workflows/post-merge.yml
vendored
|
|
@ -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
4
.gitignore
vendored
|
|
@ -90,6 +90,10 @@ node_modules
|
|||
## Rustdoc GUI tests
|
||||
tests/rustdoc-gui/src/**.lock
|
||||
|
||||
## Test dashboard
|
||||
.citool-cache/
|
||||
test-dashboard/
|
||||
|
||||
## direnv
|
||||
/.envrc
|
||||
/.direnv/
|
||||
|
|
|
|||
2
.mailmap
2
.mailmap
|
|
@ -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>
|
||||
|
|
|
|||
88
Cargo.lock
88
Cargo.lock
|
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
|||
211
RELEASES.md
211
RELEASES.md
|
|
@ -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)
|
||||
===========================
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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) =>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>>) {
|
||||
|
|
|
|||
345
compiler/rustc_ast_lowering/src/contract.rs
Normal file
345
compiler/rustc_ast_lowering/src/contract.rs
Normal 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)))
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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, .. }) => {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
21
compiler/rustc_attr_parsing/src/attributes/pin_v2.rs
Normal file
21
compiler/rustc_attr_parsing/src/attributes/pin_v2.rs
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>(
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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, ®ioncx);
|
||||
let flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx);
|
||||
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(..)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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, _) => {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(_)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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('"');
|
||||
}
|
||||
|
|
@ -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:?}");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
pub(crate) mod archive;
|
||||
mod command_line_args;
|
||||
pub(crate) mod lto;
|
||||
pub(crate) mod owned_target_machine;
|
||||
mod profiling;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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, ®ion_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) };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>> {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
])
|
||||
|
|
|
|||
|
|
@ -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 won’t 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>)>),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue