Auto merge of #18317 - lnicola:sync-from-rust, r=Veykril
minor: sync from downstream
This commit is contained in:
commit
e09bf4c07a
1490 changed files with 22828 additions and 14148 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
@ -122,6 +122,9 @@ jobs:
|
|||
# which then uses log commands to actually set them.
|
||||
EXTRA_VARIABLES: ${{ toJson(matrix.env) }}
|
||||
|
||||
- name: setup upstream remote
|
||||
run: src/ci/scripts/setup-upstream-remote.sh
|
||||
|
||||
- name: ensure the channel matches the target branch
|
||||
run: src/ci/scripts/verify-channel.sh
|
||||
|
||||
|
|
3
.mailmap
3
.mailmap
|
@ -74,6 +74,7 @@ Ben Striegel <ben.striegel@gmail.com>
|
|||
Benjamin Jackman <ben@jackman.biz>
|
||||
Benoît Cortier <benoit.cortier@fried-world.eu>
|
||||
Bheesham Persaud <bheesham123@hotmail.com> Bheesham Persaud <bheesham.persaud@live.ca>
|
||||
bjorn3 <17426603+bjorn3@users.noreply.github.com> <bjorn3@users.noreply.github.com>
|
||||
Björn Steinbrink <bsteinbr@gmail.com> <B.Steinbrink@gmx.de>
|
||||
blake2-ppc <ulrik.sverdrup@gmail.com> <blake2-ppc>
|
||||
blyxyas <blyxyas@gmail.com> Alejandra González <blyxyas@gmail.com>
|
||||
|
@ -279,6 +280,7 @@ Jerry Hardee <hardeejj9@gmail.com>
|
|||
Jesús Rubio <jesusprubio@gmail.com>
|
||||
Jethro Beekman <github@jbeekman.nl>
|
||||
Jian Zeng <knight42@mail.ustc.edu.cn>
|
||||
Jieyou Xu <jieyouxu@outlook.com> <39484203+jieyouxu@users.noreply.github.com>
|
||||
Jihyun Yu <j.yu@navercorp.com> <yjh0502@gmail.com>
|
||||
Jihyun Yu <j.yu@navercorp.com> jihyun <jihyun@nablecomm.com>
|
||||
Jihyun Yu <j.yu@navercorp.com> Jihyun Yu <jihyun@nclab.kaist.ac.kr>
|
||||
|
@ -311,6 +313,7 @@ Josh Driver <keeperofdakeys@gmail.com>
|
|||
Josh Holmer <jholmer.in@gmail.com>
|
||||
Josh Stone <cuviper@gmail.com> <jistone@redhat.com>
|
||||
Josh Stone <cuviper@gmail.com> <jistone@fedoraproject.org>
|
||||
Julia Ryan <juliaryan3.14@gmail.com> <josephryan3.14@gmail.com>
|
||||
Julian Knodt <julianknodt@gmail.com>
|
||||
jumbatm <jumbatm@gmail.com> <30644300+jumbatm@users.noreply.github.com>
|
||||
Junyoung Cho <june0.cho@samsung.com>
|
||||
|
|
101
Cargo.lock
101
Cargo.lock
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
|
@ -96,7 +96,7 @@ version = "0.9.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
"yansi-term",
|
||||
]
|
||||
|
||||
|
@ -107,7 +107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "24e35ed54e5ea7997c14ed4c70ba043478db1112e98263b3b035907aa197d991"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -168,7 +168,7 @@ dependencies = [
|
|||
"anstyle",
|
||||
"anstyle-lossy",
|
||||
"html-escape",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -377,7 +377,7 @@ dependencies = [
|
|||
"cargo_metadata",
|
||||
"directories",
|
||||
"rustc-build-sysroot",
|
||||
"rustc_tools_util",
|
||||
"rustc_tools_util 0.4.0",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -552,7 +552,7 @@ dependencies = [
|
|||
"parking_lot",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc_tools_util",
|
||||
"rustc_tools_util 0.3.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.79",
|
||||
|
@ -737,7 +737,7 @@ dependencies = [
|
|||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
|
@ -1425,7 +1425,7 @@ version = "0.2.21"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1788,7 +1788,7 @@ dependencies = [
|
|||
"instant",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2590,7 +2590,7 @@ version = "0.1.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2601,7 +2601,7 @@ checksum = "9ad43c07024ef767f9160710b3a6773976194758c7919b17e63b863db0bdf7fb"
|
|||
dependencies = [
|
||||
"bytecount",
|
||||
"fnv",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3416,6 +3416,7 @@ dependencies = [
|
|||
"measureme",
|
||||
"object 0.36.4",
|
||||
"rustc-demangle",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
"rustc_codegen_ssa",
|
||||
|
@ -3456,6 +3457,7 @@ dependencies = [
|
|||
"object 0.36.4",
|
||||
"pathdiff",
|
||||
"regex",
|
||||
"rustc_abi",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
|
@ -3493,6 +3495,7 @@ name = "rustc_const_eval"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rustc_abi",
|
||||
"rustc_apfloat",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
|
@ -3772,6 +3775,7 @@ name = "rustc_hir_typeck"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_ir",
|
||||
"rustc_attr",
|
||||
|
@ -3853,6 +3857,7 @@ dependencies = [
|
|||
"rustc_target",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
@ -4027,6 +4032,7 @@ dependencies = [
|
|||
"gsgdt",
|
||||
"polonius-engine",
|
||||
"rustc-rayon-core",
|
||||
"rustc_abi",
|
||||
"rustc_apfloat",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
|
@ -4183,7 +4189,7 @@ dependencies = [
|
|||
"thin-vec",
|
||||
"tracing",
|
||||
"unicode-normalization",
|
||||
"unicode-width",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4417,7 +4423,7 @@ dependencies = [
|
|||
"sha1",
|
||||
"sha2",
|
||||
"tracing",
|
||||
"unicode-width",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4460,6 +4466,12 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_tools_util"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3316159ab19e19d1065ecc49278e87f767a9dae9fae80348d2b4d4fa4ae02d4d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_trait_selection"
|
||||
version = "0.0.0"
|
||||
|
@ -4485,6 +4497,7 @@ dependencies = [
|
|||
"rustc_transmute",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
@ -4522,6 +4535,7 @@ name = "rustc_ty_utils"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast_ir",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
|
@ -4555,6 +4569,7 @@ dependencies = [
|
|||
"rustc_span",
|
||||
"rustc_type_ir_macros",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
@ -4681,7 +4696,7 @@ dependencies = [
|
|||
"tracing-subscriber",
|
||||
"unicode-properties",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5091,7 +5106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4c998b0c8b921495196a48aabaf1901ff28be0760136e31604f7967b0792050e"
|
||||
dependencies = [
|
||||
"papergrid",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5640,6 +5655,12 @@ version = "0.1.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
|
@ -5798,16 +5819,16 @@ checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-component-ld"
|
||||
version = "0.5.9"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fde17bc96539700198e12516230c76095cc215c84ef39ad206e1af3f84243e0f"
|
||||
checksum = "4d4aa6bd7fbe7cffbed29fe3e236fda74419def1bdef6f80f989ec51137edf44"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"lexopt",
|
||||
"tempfile",
|
||||
"wasi-preview1-component-adapter-provider",
|
||||
"wasmparser 0.218.0",
|
||||
"wasmparser 0.219.0",
|
||||
"wat",
|
||||
"wit-component",
|
||||
"wit-parser",
|
||||
|
@ -5831,19 +5852,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.218.0"
|
||||
version = "0.219.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22b896fa8ceb71091ace9bcb81e853f54043183a1c9667cf93422c40252ffa0a"
|
||||
checksum = "e2b1b95711b3ad655656a341e301cc64e33cbee94de9a99a1c5a2ab88efab79d"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
"wasmparser 0.218.0",
|
||||
"wasmparser 0.219.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.218.0"
|
||||
version = "0.219.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa5eeb071abe8a2132fdd5565dabffee70775ee8c24fc7e300ac43f51f4a8a91"
|
||||
checksum = "96132fe00dd17d092d2be289eeed5a0a68ad3cf30b68e8875bc953b96f55f0be"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
|
@ -5851,8 +5872,8 @@ dependencies = [
|
|||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"wasm-encoder 0.218.0",
|
||||
"wasmparser 0.218.0",
|
||||
"wasm-encoder 0.219.0",
|
||||
"wasmparser 0.219.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5867,9 +5888,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.218.0"
|
||||
version = "0.219.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b09e46c7fceceaa72b2dd1a8a137ea7fd8f93dfaa69806010a709918e496c5dc"
|
||||
checksum = "324b4e56d24439495b88cd81439dad5e97f3c7b1eedc3c7e10455ed1e045e9a2"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bitflags 2.6.0",
|
||||
|
@ -5881,22 +5902,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "218.0.0"
|
||||
version = "219.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a53cd1f0fa505df97557e36a58bddb8296e2fcdcd089529545ebfdb18a1b9d7"
|
||||
checksum = "06880ecb25662bc21db6a83f4fcc27c41f71fbcba4f1980b650c88ada92728e1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"leb128",
|
||||
"memchr",
|
||||
"unicode-width",
|
||||
"wasm-encoder 0.218.0",
|
||||
"unicode-width 0.1.14",
|
||||
"wasm-encoder 0.219.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.218.0"
|
||||
version = "1.219.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f87f8e14e776762e07927c27c2054d2cf678aab9aae2d431a79b3e31e4dd391"
|
||||
checksum = "11e56dbf9fc89111b0d97c91e683d7895b1a6e5633a729f2ccad2303724005b6"
|
||||
dependencies = [
|
||||
"wast",
|
||||
]
|
||||
|
@ -6173,9 +6194,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.218.0"
|
||||
version = "0.219.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa53aa7e6bf2b3e8ccaffbcc963fbdb672a603dc0af393a481b6cec24c266406"
|
||||
checksum = "99a76111c20444a814019de20499d30940ecd219b9512ee296f034a5edb18a2d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.6.0",
|
||||
|
@ -6184,17 +6205,17 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder 0.218.0",
|
||||
"wasm-encoder 0.219.0",
|
||||
"wasm-metadata",
|
||||
"wasmparser 0.218.0",
|
||||
"wasmparser 0.219.0",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.218.0"
|
||||
version = "0.219.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d3d1066ab761b115f97fef2b191090faabcb0f37b555b758d3caf42d4ed9e55"
|
||||
checksum = "23102e180c0c464f36e293d31a27b524e3ece930d7b5527d2f33f9d2c963de64"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
|
@ -6205,7 +6226,7 @@ dependencies = [
|
|||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser 0.218.0",
|
||||
"wasmparser 0.219.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
176
RELEASES.md
176
RELEASES.md
|
@ -1,3 +1,179 @@
|
|||
Version 1.82.0 (2024-10-17)
|
||||
==========================
|
||||
|
||||
<a id="1.82.0-Language"></a>
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Don't make statement nonterminals match pattern nonterminals](https://github.com/rust-lang/rust/pull/120221/)
|
||||
- [Patterns matching empty types can now be omitted in common cases](https://github.com/rust-lang/rust/pull/122792)
|
||||
- [Enforce supertrait outlives obligations when using trait impls](https://github.com/rust-lang/rust/pull/124336)
|
||||
- [`addr_of(_mut)!` macros and the newly stabilized `&raw (const|mut)` are now safe to use with all static items](https://github.com/rust-lang/rust/pull/125834)
|
||||
- [size_of_val_raw: for length 0 this is safe to call](https://github.com/rust-lang/rust/pull/126152/)
|
||||
- [Reorder trait bound modifiers *after* `for<...>` binder in trait bounds](https://github.com/rust-lang/rust/pull/127054/)
|
||||
- [Stabilize opaque type precise capturing (RFC 3617)](https://github.com/rust-lang/rust/pull/127672)
|
||||
- [Stabilize `&raw const` and `&raw mut` operators (RFC 2582)](https://github.com/rust-lang/rust/pull/127679)
|
||||
- [Stabilize unsafe extern blocks (RFC 3484)](https://github.com/rust-lang/rust/pull/127921)
|
||||
- [Stabilize nested field access in `offset_of!`](https://github.com/rust-lang/rust/pull/128284)
|
||||
- [Do not require `T` to be live when dropping `[T; 0]`](https://github.com/rust-lang/rust/pull/128438)
|
||||
- [Stabilize `const` operands in inline assembly](https://github.com/rust-lang/rust/pull/128570)
|
||||
- [Stabilize floating-point arithmetic in `const fn`](https://github.com/rust-lang/rust/pull/128596)
|
||||
- [Stabilize explicit opt-in to unsafe attributes](https://github.com/rust-lang/rust/pull/128771)
|
||||
- [Document NaN bit patterns guarantees](https://github.com/rust-lang/rust/pull/129559)
|
||||
|
||||
|
||||
<a id="1.82.0-Compiler"></a>
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [Promote riscv64gc-unknown-linux-musl to tier 2](https://github.com/rust-lang/rust/pull/122049)
|
||||
- [Promote Mac Catalyst targets `aarch64-apple-ios-macabi` and `x86_64-apple-ios-macabi` to Tier 2, and ship them with rustup](https://github.com/rust-lang/rust/pull/126450)
|
||||
- [Add tier 3 NuttX based targets for RISC-V and ARM](https://github.com/rust-lang/rust/pull/127755)
|
||||
- [Add tier 3 powerpc-unknown-linux-muslspe target](https://github.com/rust-lang/rust/pull/127905)
|
||||
- [Improved diagnostics to explain why a pattern is unreachable](https://github.com/rust-lang/rust/pull/128034)
|
||||
- [The compiler now triggers the unreachable code warning properly for async functions that don't return/are `-> !`](https://github.com/rust-lang/rust/pull/128443)
|
||||
- [Promote `aarch64-apple-darwin` to Tier 1](https://github.com/rust-lang/rust/pull/128592)
|
||||
- [Add Trusty OS target `aarch64-unknown-trusty` and `armv7-unknown-trusty` as tier 3 targets](https://github.com/rust-lang/rust/pull/129490)
|
||||
- [Promote `wasm32-wasip2` to Tier 2.](https://github.com/rust-lang/rust/pull/126967/)
|
||||
|
||||
|
||||
<a id="1.82.0-Libraries"></a>
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [Generalize `{Rc,Arc}::make_mut()` to `Path`, `OsStr`, and `CStr`.](https://github.com/rust-lang/rust/pull/126877)
|
||||
|
||||
<a id="1.82.0-Stabilized-APIs"></a>
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
|
||||
- [`std::thread::Builder::spawn_unchecked`](https://doc.rust-lang.org/stable/std/thread/struct.Builder.html#method.spawn_unchecked)
|
||||
- [`std::str::CharIndices::offset`](https://doc.rust-lang.org/nightly/std/str/struct.CharIndices.html#method.offset)
|
||||
- [`std::option::Option::is_none_or`](https://doc.rust-lang.org/nightly/std/option/enum.Option.html#method.is_none_or)
|
||||
- [`[T]::is_sorted`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.is_sorted)
|
||||
- [`[T]::is_sorted_by`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.is_sorted_by)
|
||||
- [`[T]::is_sorted_by_key`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.is_sorted_by_key)
|
||||
- [`Iterator::is_sorted`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.is_sorted)
|
||||
- [`Iterator::is_sorted_by`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.is_sorted_by)
|
||||
- [`Iterator::is_sorted_by_key`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.is_sorted_by_key)
|
||||
- [`std::future::Ready::into_inner`](https://doc.rust-lang.org/nightly/std/future/struct.Ready.html#method.into_inner)
|
||||
- [`std::iter::repeat_n`](https://doc.rust-lang.org/nightly/std/iter/fn.repeat_n.html)
|
||||
- [`impl<T: Clone> DoubleEndedIterator for Take<Repeat<T>>`](https://doc.rust-lang.org/nightly/std/iter/struct.Take.html#impl-DoubleEndedIterator-for-Take%3CRepeat%3CT%3E%3E)
|
||||
- [`impl<T: Clone> ExactSizeIterator for Take<Repeat<T>>`](https://doc.rust-lang.org/nightly/std/iter/struct.Take.html#impl-ExactSizeIterator-for-Take%3CRepeat%3CT%3E%3E)
|
||||
- [`impl<T: Clone> ExactSizeIterator for Take<RepeatWith<T>>`](https://doc.rust-lang.org/nightly/std/iter/struct.Take.html#impl-ExactSizeIterator-for-Take%3CRepeatWith%3CF%3E%3E)
|
||||
- [`impl Default for std::collections::binary_heap::Iter`](https://doc.rust-lang.org/nightly/std/collections/binary_heap/struct.Iter.html#impl-Default-for-Iter%3C'_,+T%3E)
|
||||
- [`impl Default for std::collections::btree_map::RangeMut`](https://doc.rust-lang.org/nightly/std/collections/btree_map/struct.RangeMut.html#impl-Default-for-RangeMut%3C'_,+K,+V%3E)
|
||||
- [`impl Default for std::collections::btree_map::ValuesMut`](https://doc.rust-lang.org/nightly/std/collections/btree_map/struct.ValuesMut.html#impl-Default-for-ValuesMut%3C'_,+K,+V%3E)
|
||||
- [`impl Default for std::collections::vec_deque::Iter`](https://doc.rust-lang.org/nightly/std/collections/vec_deque/struct.Iter.html#impl-Default-for-Iter%3C'_,+T%3E)
|
||||
- [`impl Default for std::collections::vec_deque::IterMut`](https://doc.rust-lang.org/nightly/std/collections/vec_deque/struct.IterMut.html#impl-Default-for-IterMut%3C'_,+T%3E)
|
||||
- [`Rc<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new_uninit)
|
||||
- [`Rc<T>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init)
|
||||
- [`Rc<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new_uninit_slice)
|
||||
- [`Rc<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init-1)
|
||||
- [`Arc<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.new_uninit)
|
||||
- [`Arc<T>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init)
|
||||
- [`Arc<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.new_uninit_slice)
|
||||
- [`Arc<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init-1)
|
||||
- [`Box<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.new_uninit)
|
||||
- [`Box<T>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init)
|
||||
- [`Box<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.new_uninit_slice)
|
||||
- [`Box<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init-1)
|
||||
- [`core::arch::x86_64::_bextri_u64`](https://doc.rust-lang.org/stable/core/arch/x86_64/fn._bextri_u64.html)
|
||||
- [`core::arch::x86_64::_bextri_u32`](https://doc.rust-lang.org/stable/core/arch/x86_64/fn._bextri_u32.html)
|
||||
- [`core::arch::x86::_mm_broadcastsi128_si256`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_broadcastsi128_si256.html)
|
||||
- [`core::arch::x86::_mm256_stream_load_si256`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm256_stream_load_si256.html)
|
||||
- [`core::arch::x86::_tzcnt_u16`](https://doc.rust-lang.org/stable/core/arch/x86/fn._tzcnt_u16.html)
|
||||
- [`core::arch::x86::_mm_extracti_si64`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_extracti_si64.html)
|
||||
- [`core::arch::x86::_mm_inserti_si64`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_inserti_si64.html)
|
||||
- [`core::arch::x86::_mm_storeu_si16`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_storeu_si16.html)
|
||||
- [`core::arch::x86::_mm_storeu_si32`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_storeu_si32.html)
|
||||
- [`core::arch::x86::_mm_storeu_si64`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_storeu_si64.html)
|
||||
- [`core::arch::x86::_mm_loadu_si16`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_loadu_si16.html)
|
||||
- [`core::arch::x86::_mm_loadu_si32`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_loadu_si32.html)
|
||||
- [`core::arch::wasm32::u8x16_relaxed_swizzle`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u8x16_relaxed_swizzle.html)
|
||||
- [`core::arch::wasm32::i8x16_relaxed_swizzle`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i8x16_relaxed_swizzle.html)
|
||||
- [`core::arch::wasm32::i32x4_relaxed_trunc_f32x4`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i32x4_relaxed_trunc_f32x4.html)
|
||||
- [`core::arch::wasm32::u32x4_relaxed_trunc_f32x4`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u32x4_relaxed_trunc_f32x4.html)
|
||||
- [`core::arch::wasm32::i32x4_relaxed_trunc_f64x2_zero`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i32x4_relaxed_trunc_f64x2_zero.html)
|
||||
- [`core::arch::wasm32::u32x4_relaxed_trunc_f64x2_zero`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u32x4_relaxed_trunc_f64x2_zero.html)
|
||||
- [`core::arch::wasm32::f32x4_relaxed_madd`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f32x4_relaxed_madd.html)
|
||||
- [`core::arch::wasm32::f32x4_relaxed_nmadd`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f32x4_relaxed_nmadd.html)
|
||||
- [`core::arch::wasm32::f64x2_relaxed_madd`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f64x2_relaxed_madd.html)
|
||||
- [`core::arch::wasm32::f64x2_relaxed_nmadd`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f64x2_relaxed_nmadd.html)
|
||||
- [`core::arch::wasm32::i8x16_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i8x16_relaxed_laneselect.html)
|
||||
- [`core::arch::wasm32::u8x16_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u8x16_relaxed_laneselect.html)
|
||||
- [`core::arch::wasm32::i16x8_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i16x8_relaxed_laneselect.html)
|
||||
- [`core::arch::wasm32::u16x8_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u16x8_relaxed_laneselect.html)
|
||||
- [`core::arch::wasm32::i32x4_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i32x4_relaxed_laneselect.html)
|
||||
- [`core::arch::wasm32::u32x4_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u32x4_relaxed_laneselect.html)
|
||||
- [`core::arch::wasm32::i64x2_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i64x2_relaxed_laneselect.html)
|
||||
- [`core::arch::wasm32::u64x2_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u64x2_relaxed_laneselect.html)
|
||||
- [`core::arch::wasm32::f32x4_relaxed_min`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f32x4_relaxed_min.html)
|
||||
- [`core::arch::wasm32::f32x4_relaxed_max`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f32x4_relaxed_max.html)
|
||||
- [`core::arch::wasm32::f64x2_relaxed_min`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f64x2_relaxed_min.html)
|
||||
- [`core::arch::wasm32::f64x2_relaxed_max`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f64x2_relaxed_max.html)
|
||||
- [`core::arch::wasm32::i16x8_relaxed_q15mulr`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i16x8_relaxed_q15mulr.html)
|
||||
- [`core::arch::wasm32::u16x8_relaxed_q15mulr`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u16x8_relaxed_q15mulr.html)
|
||||
- [`core::arch::wasm32::i16x8_relaxed_dot_i8x16_i7x16`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i16x8_relaxed_dot_i8x16_i7x16.html)
|
||||
- [`core::arch::wasm32::u16x8_relaxed_dot_i8x16_i7x16`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u16x8_relaxed_dot_i8x16_i7x16.html)
|
||||
- [`core::arch::wasm32::i32x4_relaxed_dot_i8x16_i7x16_add`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i32x4_relaxed_dot_i8x16_i7x16_add.html)
|
||||
- [`core::arch::wasm32::u32x4_relaxed_dot_i8x16_i7x16_add`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u32x4_relaxed_dot_i8x16_i7x16_add.html)
|
||||
|
||||
These APIs are now stable in const contexts:
|
||||
|
||||
- [`std::task::Waker::from_raw`](https://doc.rust-lang.org/nightly/std/task/struct.Waker.html#method.from_raw)
|
||||
- [`std::task::Context::from_waker`](https://doc.rust-lang.org/nightly/std/task/struct.Context.html#method.from_waker)
|
||||
- [`std::task::Context::waker`](https://doc.rust-lang.org/nightly/std/task/struct.Context.html#method.waker)
|
||||
- [`$integer::from_str_radix`](https://doc.rust-lang.org/nightly/std/primitive.u32.html#method.from_str_radix)
|
||||
- [`std::num::ParseIntError::kind`](https://doc.rust-lang.org/nightly/std/num/struct.ParseIntError.html#method.kind)
|
||||
|
||||
<a id="1.82.0-Cargo"></a>
|
||||
|
||||
Cargo
|
||||
-----
|
||||
- [feat: Add `info` cargo subcommand](https://github.com/rust-lang/cargo/pull/14141/)
|
||||
|
||||
<a id="1.82.0-Compatibility-Notes"></a>
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- We now [disallow setting some built-in cfgs via the command-line](https://github.com/rust-lang/rust/pull/126158) with the newly added [`explicit_builtin_cfgs_in_flags`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#explicit-builtin-cfgs-in-flags) lint in order to prevent incoherent state, eg. `windows` cfg active but target is Linux based. The appropriate [`rustc` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html) should be used instead.
|
||||
- The standard library has a new implementation of `binary_search` which is significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation.
|
||||
- [illumos/Solaris now sets `MSG_NOSIGNAL` when writing to sockets](https://github.com/rust-lang/rust/pull/128259). This avoids killing the process with SIGPIPE when writing to a closed socket, which matches the existing behavior on other UNIX targets.
|
||||
- [Removes a problematic hack that always passed the --whole-archive linker flag for tests, which may cause linker errors for code accidentally relying on it.](https://github.com/rust-lang/rust/pull/128400)
|
||||
- The WebAssembly target features `multivalue` and `reference-types` are now
|
||||
both enabled by default. These two features both have subtle changes implied
|
||||
for generated WebAssembly binaries. For the `multivalue` feature, WebAssembly
|
||||
target support has changed when upgrading to LLVM 19. Support for generating
|
||||
functions with multiple returns no longer works and
|
||||
`-Ctarget-feature=+multivalue` has a different meaning than it did in LLVM 18
|
||||
and prior. There is no longer any supported means to generate a module that has
|
||||
a function with multiple returns in WebAssembly from Rust source code. For the
|
||||
`reference-types` feature the encoding of immediates in the `call_indirect`, a
|
||||
commonly used instruction by the WebAssembly backend, has changed. Validators
|
||||
and parsers which don't understand the `reference-types` proposal will no
|
||||
longer accept modules produced by LLVM due to this change in encoding of
|
||||
immediates. Additionally these features being enabled are encoded in the
|
||||
`target_features` custom section and may affect downstream tooling such as
|
||||
`wasm-opt` consuming the module. Generating a WebAssembly module that disables
|
||||
default features requires `-Zbuild-std` support from Cargo and more information
|
||||
can be found at
|
||||
[rust-lang/rust#128511](https://github.com/rust-lang/rust/pull/128511).
|
||||
- [Rust now raises unsafety errors for union patterns in parameter-position](https://github.com/rust-lang/rust/pull/130531)
|
||||
|
||||
|
||||
<a id="1.82.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 19](https://github.com/rust-lang/rust/pull/127513)
|
||||
|
||||
Version 1.81.0 (2024-09-05)
|
||||
==========================
|
||||
|
||||
|
|
254
compiler/rustc_abi/src/callconv.rs
Normal file
254
compiler/rustc_abi/src/callconv.rs
Normal file
|
@ -0,0 +1,254 @@
|
|||
mod abi {
|
||||
pub(crate) use crate::Primitive::*;
|
||||
pub(crate) use crate::Variants;
|
||||
}
|
||||
|
||||
use rustc_macros::HashStable_Generic;
|
||||
|
||||
use crate::{Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||
pub enum RegKind {
|
||||
Integer,
|
||||
Float,
|
||||
Vector,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||
pub struct Reg {
|
||||
pub kind: RegKind,
|
||||
pub size: Size,
|
||||
}
|
||||
|
||||
macro_rules! reg_ctor {
|
||||
($name:ident, $kind:ident, $bits:expr) => {
|
||||
pub fn $name() -> Reg {
|
||||
Reg { kind: RegKind::$kind, size: Size::from_bits($bits) }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Reg {
|
||||
reg_ctor!(i8, Integer, 8);
|
||||
reg_ctor!(i16, Integer, 16);
|
||||
reg_ctor!(i32, Integer, 32);
|
||||
reg_ctor!(i64, Integer, 64);
|
||||
reg_ctor!(i128, Integer, 128);
|
||||
|
||||
reg_ctor!(f32, Float, 32);
|
||||
reg_ctor!(f64, Float, 64);
|
||||
}
|
||||
|
||||
impl Reg {
|
||||
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
|
||||
let dl = cx.data_layout();
|
||||
match self.kind {
|
||||
RegKind::Integer => match self.size.bits() {
|
||||
1 => dl.i1_align.abi,
|
||||
2..=8 => dl.i8_align.abi,
|
||||
9..=16 => dl.i16_align.abi,
|
||||
17..=32 => dl.i32_align.abi,
|
||||
33..=64 => dl.i64_align.abi,
|
||||
65..=128 => dl.i128_align.abi,
|
||||
_ => panic!("unsupported integer: {self:?}"),
|
||||
},
|
||||
RegKind::Float => match self.size.bits() {
|
||||
16 => dl.f16_align.abi,
|
||||
32 => dl.f32_align.abi,
|
||||
64 => dl.f64_align.abi,
|
||||
128 => dl.f128_align.abi,
|
||||
_ => panic!("unsupported float: {self:?}"),
|
||||
},
|
||||
RegKind::Vector => dl.vector_align(self.size).abi,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return value from the `homogeneous_aggregate` test function.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum HomogeneousAggregate {
|
||||
/// Yes, all the "leaf fields" of this struct are passed in the
|
||||
/// same way (specified in the `Reg` value).
|
||||
Homogeneous(Reg),
|
||||
|
||||
/// There are no leaf fields at all.
|
||||
NoData,
|
||||
}
|
||||
|
||||
/// Error from the `homogeneous_aggregate` test function, indicating
|
||||
/// there are distinct leaf fields passed in different ways,
|
||||
/// or this is uninhabited.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Heterogeneous;
|
||||
|
||||
impl HomogeneousAggregate {
|
||||
/// If this is a homogeneous aggregate, returns the homogeneous
|
||||
/// unit, else `None`.
|
||||
pub fn unit(self) -> Option<Reg> {
|
||||
match self {
|
||||
HomogeneousAggregate::Homogeneous(reg) => Some(reg),
|
||||
HomogeneousAggregate::NoData => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in
|
||||
/// the same `struct`. Only succeeds if only one of them has any data,
|
||||
/// or both units are identical.
|
||||
fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> {
|
||||
match (self, other) {
|
||||
(x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x),
|
||||
|
||||
(HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => {
|
||||
if a != b {
|
||||
return Err(Heterogeneous);
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Ty> TyAndLayout<'a, Ty> {
|
||||
/// Returns `true` if this is an aggregate type (including a ScalarPair!)
|
||||
pub fn is_aggregate(&self) -> bool {
|
||||
match self.abi {
|
||||
Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false,
|
||||
Abi::ScalarPair(..) | Abi::Aggregate { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Homogeneous` if this layout is an aggregate containing fields of
|
||||
/// only a single type (e.g., `(u32, u32)`). Such aggregates are often
|
||||
/// special-cased in ABIs.
|
||||
///
|
||||
/// Note: We generally ignore 1-ZST fields when computing this value (see #56877).
|
||||
///
|
||||
/// This is public so that it can be used in unit tests, but
|
||||
/// should generally only be relevant to the ABI details of
|
||||
/// specific targets.
|
||||
pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
match self.abi {
|
||||
Abi::Uninhabited => Err(Heterogeneous),
|
||||
|
||||
// The primitive for this algorithm.
|
||||
Abi::Scalar(scalar) => {
|
||||
let kind = match scalar.primitive() {
|
||||
abi::Int(..) | abi::Pointer(_) => RegKind::Integer,
|
||||
abi::Float(_) => RegKind::Float,
|
||||
};
|
||||
Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
|
||||
}
|
||||
|
||||
Abi::Vector { .. } => {
|
||||
assert!(!self.is_zst());
|
||||
Ok(HomogeneousAggregate::Homogeneous(Reg {
|
||||
kind: RegKind::Vector,
|
||||
size: self.size,
|
||||
}))
|
||||
}
|
||||
|
||||
Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => {
|
||||
// Helper for computing `homogeneous_aggregate`, allowing a custom
|
||||
// starting offset (used below for handling variants).
|
||||
let from_fields_at =
|
||||
|layout: Self,
|
||||
start: Size|
|
||||
-> Result<(HomogeneousAggregate, Size), Heterogeneous> {
|
||||
let is_union = match layout.fields {
|
||||
FieldsShape::Primitive => {
|
||||
unreachable!("aggregates can't have `FieldsShape::Primitive`")
|
||||
}
|
||||
FieldsShape::Array { count, .. } => {
|
||||
assert_eq!(start, Size::ZERO);
|
||||
|
||||
let result = if count > 0 {
|
||||
layout.field(cx, 0).homogeneous_aggregate(cx)?
|
||||
} else {
|
||||
HomogeneousAggregate::NoData
|
||||
};
|
||||
return Ok((result, layout.size));
|
||||
}
|
||||
FieldsShape::Union(_) => true,
|
||||
FieldsShape::Arbitrary { .. } => false,
|
||||
};
|
||||
|
||||
let mut result = HomogeneousAggregate::NoData;
|
||||
let mut total = start;
|
||||
|
||||
for i in 0..layout.fields.count() {
|
||||
let field = layout.field(cx, i);
|
||||
if field.is_1zst() {
|
||||
// No data here and no impact on layout, can be ignored.
|
||||
// (We might be able to also ignore all aligned ZST but that's less clear.)
|
||||
continue;
|
||||
}
|
||||
|
||||
if !is_union && total != layout.fields.offset(i) {
|
||||
// This field isn't just after the previous one we considered, abort.
|
||||
return Err(Heterogeneous);
|
||||
}
|
||||
|
||||
result = result.merge(field.homogeneous_aggregate(cx)?)?;
|
||||
|
||||
// Keep track of the offset (without padding).
|
||||
let size = field.size;
|
||||
if is_union {
|
||||
total = total.max(size);
|
||||
} else {
|
||||
total += size;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((result, total))
|
||||
};
|
||||
|
||||
let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
|
||||
|
||||
match &self.variants {
|
||||
abi::Variants::Single { .. } => {}
|
||||
abi::Variants::Multiple { variants, .. } => {
|
||||
// Treat enum variants like union members.
|
||||
// HACK(eddyb) pretend the `enum` field (discriminant)
|
||||
// is at the start of every variant (otherwise the gap
|
||||
// at the start of all variants would disqualify them).
|
||||
//
|
||||
// NB: for all tagged `enum`s (which include all non-C-like
|
||||
// `enum`s with defined FFI representation), this will
|
||||
// match the homogeneous computation on the equivalent
|
||||
// `struct { tag; union { variant1; ... } }` and/or
|
||||
// `union { struct { tag; variant1; } ... }`
|
||||
// (the offsets of variant fields should be identical
|
||||
// between the two for either to be a homogeneous aggregate).
|
||||
let variant_start = total;
|
||||
for variant_idx in variants.indices() {
|
||||
let (variant_result, variant_total) =
|
||||
from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;
|
||||
|
||||
result = result.merge(variant_result)?;
|
||||
total = total.max(variant_total);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There needs to be no padding.
|
||||
if total != self.size {
|
||||
Err(Heterogeneous)
|
||||
} else {
|
||||
match result {
|
||||
HomogeneousAggregate::Homogeneous(_) => {
|
||||
assert_ne!(total, Size::ZERO);
|
||||
}
|
||||
HomogeneousAggregate::NoData => {
|
||||
assert_eq!(total, Size::ZERO);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
Abi::Aggregate { sized: false } => Err(Heterogeneous),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,10 @@ use crate::{
|
|||
Variants, WrappingRange,
|
||||
};
|
||||
|
||||
mod ty;
|
||||
|
||||
pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
|
||||
|
||||
// A variant is absent if it's uninhabited and only has ZST fields.
|
||||
// Present uninhabited variants only require space for their fields,
|
||||
// but *not* an encoding of the discriminant (e.g., a tag value).
|
||||
|
|
|
@ -1,24 +1,13 @@
|
|||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub use Float::*;
|
||||
pub use Integer::*;
|
||||
pub use Primitive::*;
|
||||
use Float::*;
|
||||
use Primitive::*;
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_macros::HashStable_Generic;
|
||||
|
||||
use crate::json::{Json, ToJson};
|
||||
|
||||
pub mod call;
|
||||
|
||||
// Explicitly import `Float` to avoid ambiguity with `Primitive::Float`.
|
||||
pub use rustc_abi::{Float, *};
|
||||
|
||||
impl ToJson for Endian {
|
||||
fn to_json(&self) -> Json {
|
||||
self.as_str().to_json()
|
||||
}
|
||||
}
|
||||
use crate::{Float, *};
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// The *source-order* index of a field in a variant.
|
|
@ -1,6 +1,7 @@
|
|||
// tidy-alphabetical-start
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(feature = "nightly", doc(rust_logo))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
|
||||
#![cfg_attr(feature = "nightly", feature(step_trait))]
|
||||
#![warn(unreachable_pub)]
|
||||
|
@ -22,11 +23,16 @@ use rustc_macros::HashStable_Generic;
|
|||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::{Decodable_Generic, Encodable_Generic};
|
||||
|
||||
mod callconv;
|
||||
mod layout;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use layout::{LayoutCalculator, LayoutCalculatorError};
|
||||
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
|
||||
pub use layout::{
|
||||
FIRST_VARIANT, FieldIdx, Layout, LayoutCalculator, LayoutCalculatorError, TyAbiInterface,
|
||||
TyAndLayout, VariantIdx,
|
||||
};
|
||||
|
||||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
||||
|
|
|
@ -23,7 +23,7 @@ use std::{cmp, fmt, mem};
|
|||
|
||||
pub use GenericArgs::*;
|
||||
pub use UnsafeSource::*;
|
||||
pub use rustc_ast_ir::{Movability, Mutability};
|
||||
pub use rustc_ast_ir::{Movability, Mutability, Pinnedness};
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
|
@ -308,7 +308,7 @@ impl TraitBoundModifiers {
|
|||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub enum GenericBound {
|
||||
Trait(PolyTraitRef, TraitBoundModifiers),
|
||||
Trait(PolyTraitRef),
|
||||
Outlives(Lifetime),
|
||||
/// Precise capturing syntax: `impl Sized + use<'a>`
|
||||
Use(ThinVec<PreciseCapturingArg>, Span),
|
||||
|
@ -1213,10 +1213,12 @@ impl Expr {
|
|||
|
||||
pub fn to_bound(&self) -> Option<GenericBound> {
|
||||
match &self.kind {
|
||||
ExprKind::Path(None, path) => Some(GenericBound::Trait(
|
||||
PolyTraitRef::new(ThinVec::new(), path.clone(), self.span),
|
||||
ExprKind::Path(None, path) => Some(GenericBound::Trait(PolyTraitRef::new(
|
||||
ThinVec::new(),
|
||||
path.clone(),
|
||||
TraitBoundModifiers::NONE,
|
||||
)),
|
||||
self.span,
|
||||
))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -2161,16 +2163,16 @@ pub enum TyKind {
|
|||
Ptr(MutTy),
|
||||
/// A reference (`&'a T` or `&'a mut T`).
|
||||
Ref(Option<Lifetime>, MutTy),
|
||||
/// A pinned reference (`&'a pin const T` or `&'a pin mut T`).
|
||||
///
|
||||
/// Desugars into `Pin<&'a T>` or `Pin<&'a mut T>`.
|
||||
PinnedRef(Option<Lifetime>, MutTy),
|
||||
/// A bare function (e.g., `fn(usize) -> bool`).
|
||||
BareFn(P<BareFnTy>),
|
||||
/// The never type (`!`).
|
||||
Never,
|
||||
/// A tuple (`(A, B, C, D,...)`).
|
||||
Tup(ThinVec<P<Ty>>),
|
||||
/// An anonymous struct type i.e. `struct { foo: Type }`.
|
||||
AnonStruct(NodeId, ThinVec<FieldDef>),
|
||||
/// An anonymous union type i.e. `union { bar: Type }`.
|
||||
AnonUnion(NodeId, ThinVec<FieldDef>),
|
||||
/// A path (`module::module::...::Type`), optionally
|
||||
/// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
|
||||
///
|
||||
|
@ -2227,10 +2229,6 @@ impl TyKind {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_anon_adt(&self) -> bool {
|
||||
matches!(self, TyKind::AnonStruct(..) | TyKind::AnonUnion(..))
|
||||
}
|
||||
}
|
||||
|
||||
/// Syntax used to declare a trait object.
|
||||
|
@ -2509,7 +2507,10 @@ impl Param {
|
|||
if ident.name == kw::SelfLower {
|
||||
return match self.ty.kind {
|
||||
TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
|
||||
TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => {
|
||||
TyKind::Ref(lt, MutTy { ref ty, mutbl })
|
||||
| TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
|
||||
if ty.kind.is_implicit_self() =>
|
||||
{
|
||||
Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
|
||||
}
|
||||
_ => Some(respan(
|
||||
|
@ -2973,6 +2974,9 @@ pub struct PolyTraitRef {
|
|||
/// The `'a` in `for<'a> Foo<&'a T>`.
|
||||
pub bound_generic_params: ThinVec<GenericParam>,
|
||||
|
||||
// Optional constness, asyncness, or polarity.
|
||||
pub modifiers: TraitBoundModifiers,
|
||||
|
||||
/// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.
|
||||
pub trait_ref: TraitRef,
|
||||
|
||||
|
@ -2980,9 +2984,15 @@ pub struct PolyTraitRef {
|
|||
}
|
||||
|
||||
impl PolyTraitRef {
|
||||
pub fn new(generic_params: ThinVec<GenericParam>, path: Path, span: Span) -> Self {
|
||||
pub fn new(
|
||||
generic_params: ThinVec<GenericParam>,
|
||||
path: Path,
|
||||
modifiers: TraitBoundModifiers,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
PolyTraitRef {
|
||||
bound_generic_params: generic_params,
|
||||
modifiers,
|
||||
trait_ref: TraitRef { path, ref_id: DUMMY_NODE_ID },
|
||||
span,
|
||||
}
|
||||
|
|
283
compiler/rustc_ast/src/expand/autodiff_attrs.rs
Normal file
283
compiler/rustc_ast/src/expand/autodiff_attrs.rs
Normal file
|
@ -0,0 +1,283 @@
|
|||
//! This crate handles the user facing autodiff macro. For each `#[autodiff(...)]` attribute,
|
||||
//! we create an [`AutoDiffItem`] which contains the source and target function names. The source
|
||||
//! is the function to which the autodiff attribute is applied, and the target is the function
|
||||
//! getting generated by us (with a name given by the user as the first autodiff arg).
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::expand::typetree::TypeTree;
|
||||
use crate::expand::{Decodable, Encodable, HashStable_Generic};
|
||||
use crate::ptr::P;
|
||||
use crate::{Ty, TyKind};
|
||||
|
||||
/// Forward and Reverse Mode are well known names for automatic differentiation implementations.
|
||||
/// Enzyme does support both, but with different semantics, see DiffActivity. The First variants
|
||||
/// are a hack to support higher order derivatives. We need to compute first order derivatives
|
||||
/// before we compute second order derivatives, otherwise we would differentiate our placeholder
|
||||
/// functions. The proper solution is to recognize and resolve this DAG of autodiff invocations,
|
||||
/// as it's already done in the C++ and Julia frontend of Enzyme.
|
||||
///
|
||||
/// (FIXME) remove *First variants.
|
||||
/// Documentation for using [reverse](https://enzyme.mit.edu/rust/rev.html) and
|
||||
/// [forward](https://enzyme.mit.edu/rust/fwd.html) mode is available online.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum DiffMode {
|
||||
/// No autodiff is applied (used during error handling).
|
||||
Error,
|
||||
/// The primal function which we will differentiate.
|
||||
Source,
|
||||
/// The target function, to be created using forward mode AD.
|
||||
Forward,
|
||||
/// The target function, to be created using reverse mode AD.
|
||||
Reverse,
|
||||
/// The target function, to be created using forward mode AD.
|
||||
/// This target function will also be used as a source for higher order derivatives,
|
||||
/// so compute it before all Forward/Reverse targets and optimize it through llvm.
|
||||
ForwardFirst,
|
||||
/// The target function, to be created using reverse mode AD.
|
||||
/// This target function will also be used as a source for higher order derivatives,
|
||||
/// so compute it before all Forward/Reverse targets and optimize it through llvm.
|
||||
ReverseFirst,
|
||||
}
|
||||
|
||||
/// Dual and Duplicated (and their Only variants) are getting lowered to the same Enzyme Activity.
|
||||
/// However, under forward mode we overwrite the previous shadow value, while for reverse mode
|
||||
/// we add to the previous shadow value. To not surprise users, we picked different names.
|
||||
/// Dual numbers is also a quite well known name for forward mode AD types.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum DiffActivity {
|
||||
/// Implicit or Explicit () return type, so a special case of Const.
|
||||
None,
|
||||
/// Don't compute derivatives with respect to this input/output.
|
||||
Const,
|
||||
/// Reverse Mode, Compute derivatives for this scalar input/output.
|
||||
Active,
|
||||
/// Reverse Mode, Compute derivatives for this scalar output, but don't compute
|
||||
/// the original return value.
|
||||
ActiveOnly,
|
||||
/// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
|
||||
/// with it.
|
||||
Dual,
|
||||
/// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
|
||||
/// with it. Drop the code which updates the original input/output for maximum performance.
|
||||
DualOnly,
|
||||
/// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument.
|
||||
Duplicated,
|
||||
/// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument.
|
||||
/// Drop the code which updates the original input for maximum performance.
|
||||
DuplicatedOnly,
|
||||
/// All Integers must be Const, but these are used to mark the integer which represents the
|
||||
/// length of a slice/vec. This is used for safety checks on slices.
|
||||
FakeActivitySize,
|
||||
}
|
||||
/// We generate one of these structs for each `#[autodiff(...)]` attribute.
|
||||
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub struct AutoDiffItem {
|
||||
/// The name of the function getting differentiated
|
||||
pub source: String,
|
||||
/// The name of the function being generated
|
||||
pub target: String,
|
||||
pub attrs: AutoDiffAttrs,
|
||||
/// Describe the memory layout of input types
|
||||
pub inputs: Vec<TypeTree>,
|
||||
/// Describe the memory layout of the output type
|
||||
pub output: TypeTree,
|
||||
}
|
||||
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub struct AutoDiffAttrs {
|
||||
/// Conceptually either forward or reverse mode AD, as described in various autodiff papers and
|
||||
/// e.g. in the [JAX
|
||||
/// Documentation](https://jax.readthedocs.io/en/latest/_tutorials/advanced-autodiff.html#how-it-s-made-two-foundational-autodiff-functions).
|
||||
pub mode: DiffMode,
|
||||
pub ret_activity: DiffActivity,
|
||||
pub input_activity: Vec<DiffActivity>,
|
||||
}
|
||||
|
||||
impl DiffMode {
|
||||
pub fn is_rev(&self) -> bool {
|
||||
matches!(self, DiffMode::Reverse | DiffMode::ReverseFirst)
|
||||
}
|
||||
pub fn is_fwd(&self) -> bool {
|
||||
matches!(self, DiffMode::Forward | DiffMode::ForwardFirst)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DiffMode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
DiffMode::Error => write!(f, "Error"),
|
||||
DiffMode::Source => write!(f, "Source"),
|
||||
DiffMode::Forward => write!(f, "Forward"),
|
||||
DiffMode::Reverse => write!(f, "Reverse"),
|
||||
DiffMode::ForwardFirst => write!(f, "ForwardFirst"),
|
||||
DiffMode::ReverseFirst => write!(f, "ReverseFirst"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Active(Only) is valid in reverse-mode AD for scalar float returns (f16/f32/...).
|
||||
/// Dual(Only) is valid in forward-mode AD for scalar float returns (f16/f32/...).
|
||||
/// Const is valid for all cases and means that we don't compute derivatives wrt. this output.
|
||||
/// That usually means we have a &mut or *mut T output and compute derivatives wrt. that arg,
|
||||
/// but this is too complex to verify here. Also it's just a logic error if users get this wrong.
|
||||
pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool {
|
||||
if activity == DiffActivity::None {
|
||||
// Only valid if primal returns (), but we can't check that here.
|
||||
return true;
|
||||
}
|
||||
match mode {
|
||||
DiffMode::Error => false,
|
||||
DiffMode::Source => false,
|
||||
DiffMode::Forward | DiffMode::ForwardFirst => {
|
||||
activity == DiffActivity::Dual
|
||||
|| activity == DiffActivity::DualOnly
|
||||
|| activity == DiffActivity::Const
|
||||
}
|
||||
DiffMode::Reverse | DiffMode::ReverseFirst => {
|
||||
activity == DiffActivity::Const
|
||||
|| activity == DiffActivity::Active
|
||||
|| activity == DiffActivity::ActiveOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For indirections (ptr/ref) we can't use Active, since Active allocates a shadow value
|
||||
/// for the given argument, but we generally can't know the size of such a type.
|
||||
/// For scalar types (f16/f32/f64/f128) we can use Active and we can't use Duplicated,
|
||||
/// since Duplicated expects a mutable ref/ptr and we would thus end up with a shadow value
|
||||
/// who is an indirect type, which doesn't match the primal scalar type. We can't prevent
|
||||
/// users here from marking scalars as Duplicated, due to type aliases.
|
||||
pub fn valid_ty_for_activity(ty: &P<Ty>, activity: DiffActivity) -> bool {
|
||||
use DiffActivity::*;
|
||||
// It's always allowed to mark something as Const, since we won't compute derivatives wrt. it.
|
||||
if matches!(activity, Const) {
|
||||
return true;
|
||||
}
|
||||
if matches!(activity, Dual | DualOnly) {
|
||||
return true;
|
||||
}
|
||||
// FIXME(ZuseZ4) We should make this more robust to also
|
||||
// handle type aliases. Once that is done, we can be more restrictive here.
|
||||
if matches!(activity, Active | ActiveOnly) {
|
||||
return true;
|
||||
}
|
||||
matches!(ty.kind, TyKind::Ptr(_) | TyKind::Ref(..))
|
||||
&& matches!(activity, Duplicated | DuplicatedOnly)
|
||||
}
|
||||
pub fn valid_input_activity(mode: DiffMode, activity: DiffActivity) -> bool {
|
||||
use DiffActivity::*;
|
||||
return match mode {
|
||||
DiffMode::Error => false,
|
||||
DiffMode::Source => false,
|
||||
DiffMode::Forward | DiffMode::ForwardFirst => {
|
||||
matches!(activity, Dual | DualOnly | Const)
|
||||
}
|
||||
DiffMode::Reverse | DiffMode::ReverseFirst => {
|
||||
matches!(activity, Active | ActiveOnly | Duplicated | DuplicatedOnly | Const)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Display for DiffActivity {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DiffActivity::None => write!(f, "None"),
|
||||
DiffActivity::Const => write!(f, "Const"),
|
||||
DiffActivity::Active => write!(f, "Active"),
|
||||
DiffActivity::ActiveOnly => write!(f, "ActiveOnly"),
|
||||
DiffActivity::Dual => write!(f, "Dual"),
|
||||
DiffActivity::DualOnly => write!(f, "DualOnly"),
|
||||
DiffActivity::Duplicated => write!(f, "Duplicated"),
|
||||
DiffActivity::DuplicatedOnly => write!(f, "DuplicatedOnly"),
|
||||
DiffActivity::FakeActivitySize => write!(f, "FakeActivitySize"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for DiffMode {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<DiffMode, ()> {
|
||||
match s {
|
||||
"Error" => Ok(DiffMode::Error),
|
||||
"Source" => Ok(DiffMode::Source),
|
||||
"Forward" => Ok(DiffMode::Forward),
|
||||
"Reverse" => Ok(DiffMode::Reverse),
|
||||
"ForwardFirst" => Ok(DiffMode::ForwardFirst),
|
||||
"ReverseFirst" => Ok(DiffMode::ReverseFirst),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FromStr for DiffActivity {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<DiffActivity, ()> {
|
||||
match s {
|
||||
"None" => Ok(DiffActivity::None),
|
||||
"Active" => Ok(DiffActivity::Active),
|
||||
"ActiveOnly" => Ok(DiffActivity::ActiveOnly),
|
||||
"Const" => Ok(DiffActivity::Const),
|
||||
"Dual" => Ok(DiffActivity::Dual),
|
||||
"DualOnly" => Ok(DiffActivity::DualOnly),
|
||||
"Duplicated" => Ok(DiffActivity::Duplicated),
|
||||
"DuplicatedOnly" => Ok(DiffActivity::DuplicatedOnly),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoDiffAttrs {
|
||||
pub fn has_ret_activity(&self) -> bool {
|
||||
self.ret_activity != DiffActivity::None
|
||||
}
|
||||
pub fn has_active_only_ret(&self) -> bool {
|
||||
self.ret_activity == DiffActivity::ActiveOnly
|
||||
}
|
||||
|
||||
pub fn error() -> Self {
|
||||
AutoDiffAttrs {
|
||||
mode: DiffMode::Error,
|
||||
ret_activity: DiffActivity::None,
|
||||
input_activity: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn source() -> Self {
|
||||
AutoDiffAttrs {
|
||||
mode: DiffMode::Source,
|
||||
ret_activity: DiffActivity::None,
|
||||
input_activity: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.mode != DiffMode::Error
|
||||
}
|
||||
|
||||
pub fn is_source(&self) -> bool {
|
||||
self.mode == DiffMode::Source
|
||||
}
|
||||
pub fn apply_autodiff(&self) -> bool {
|
||||
!matches!(self.mode, DiffMode::Error | DiffMode::Source)
|
||||
}
|
||||
|
||||
pub fn into_item(
|
||||
self,
|
||||
source: String,
|
||||
target: String,
|
||||
inputs: Vec<TypeTree>,
|
||||
output: TypeTree,
|
||||
) -> AutoDiffItem {
|
||||
AutoDiffItem { source, target, inputs, output, attrs: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AutoDiffItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Differentiating {} -> {}", self.source, self.target)?;
|
||||
write!(f, " with attributes: {:?}", self.attrs)?;
|
||||
write!(f, " with inputs: {:?}", self.inputs)?;
|
||||
write!(f, " with output: {:?}", self.output)
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ use rustc_span::symbol::Ident;
|
|||
use crate::MetaItem;
|
||||
|
||||
pub mod allocator;
|
||||
pub mod autodiff_attrs;
|
||||
pub mod typetree;
|
||||
|
||||
#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
|
||||
pub struct StrippedCfgItem<ModId = DefId> {
|
||||
|
|
90
compiler/rustc_ast/src/expand/typetree.rs
Normal file
90
compiler/rustc_ast/src/expand/typetree.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
//! This module contains the definition of the `TypeTree` and `Type` structs.
|
||||
//! They are thin Rust wrappers around the TypeTrees used by Enzyme as the LLVM based autodiff
|
||||
//! backend. The Enzyme TypeTrees currently have various limitations and should be rewritten, so the
|
||||
//! Rust frontend obviously has the same limitations. The main motivation of TypeTrees is to
|
||||
//! represent how a type looks like "in memory". Enzyme can deduce this based on usage patterns in
|
||||
//! the user code, but this is extremely slow and not even always sufficient. As such we lower some
|
||||
//! information from rustc to help Enzyme. For a full explanation of their design it is necessary to
|
||||
//! analyze the implementation in Enzyme core itself. As a rough summary, `-1` in Enzyme speech means
|
||||
//! everywhere. That is `{0:-1: Float}` means at index 0 you have a ptr, if you dereference it it
|
||||
//! will be floats everywhere. Thus `* f32`. If you have `{-1:int}` it means int's everywhere,
|
||||
//! e.g. [i32; N]. `{0:-1:-1 float}` then means one pointer at offset 0, if you dereference it there
|
||||
//! will be only pointers, if you dereference these new pointers they will point to array of floats.
|
||||
//! Generally, it allows byte-specific descriptions.
|
||||
//! FIXME: This description might be partly inaccurate and should be extended, along with
|
||||
//! adding documentation to the corresponding Enzyme core code.
|
||||
//! FIXME: Rewrite the TypeTree logic in Enzyme core to reduce the need for the rustc frontend to
|
||||
//! provide typetree information.
|
||||
//! FIXME: We should also re-evaluate where we create TypeTrees from Rust types, since MIR
|
||||
//! representations of some types might not be accurate. For example a vector of floats might be
|
||||
//! represented as a vector of u8s in MIR in some cases.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::expand::{Decodable, Encodable, HashStable_Generic};
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum Kind {
|
||||
Anything,
|
||||
Integer,
|
||||
Pointer,
|
||||
Half,
|
||||
Float,
|
||||
Double,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub struct TypeTree(pub Vec<Type>);
|
||||
|
||||
impl TypeTree {
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
pub fn all_ints() -> Self {
|
||||
Self(vec![Type { offset: -1, size: 1, kind: Kind::Integer, child: TypeTree::new() }])
|
||||
}
|
||||
pub fn int(size: usize) -> Self {
|
||||
let mut ints = Vec::with_capacity(size);
|
||||
for i in 0..size {
|
||||
ints.push(Type {
|
||||
offset: i as isize,
|
||||
size: 1,
|
||||
kind: Kind::Integer,
|
||||
child: TypeTree::new(),
|
||||
});
|
||||
}
|
||||
Self(ints)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub struct FncTree {
|
||||
pub args: Vec<TypeTree>,
|
||||
pub ret: TypeTree,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub struct Type {
|
||||
pub offset: isize,
|
||||
pub size: usize,
|
||||
pub kind: Kind,
|
||||
pub child: TypeTree,
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn add_offset(self, add: isize) -> Self {
|
||||
let offset = match self.offset {
|
||||
-1 => add,
|
||||
x => add + x,
|
||||
};
|
||||
|
||||
Self { size: self.size, kind: self.kind, child: self.child, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
|
@ -485,7 +485,7 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
|
|||
}
|
||||
TyKind::Slice(ty) => vis.visit_ty(ty),
|
||||
TyKind::Ptr(mt) => vis.visit_mt(mt),
|
||||
TyKind::Ref(lt, mt) => {
|
||||
TyKind::Ref(lt, mt) | TyKind::PinnedRef(lt, mt) => {
|
||||
visit_opt(lt, |lt| vis.visit_lifetime(lt));
|
||||
vis.visit_mt(mt);
|
||||
}
|
||||
|
@ -519,10 +519,6 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
|
|||
visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Impl));
|
||||
}
|
||||
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
|
||||
TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
|
||||
vis.visit_id(id);
|
||||
fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
|
||||
}
|
||||
}
|
||||
visit_lazy_tts(vis, tokens);
|
||||
vis.visit_span(span);
|
||||
|
@ -917,7 +913,7 @@ fn walk_fn_ret_ty<T: MutVisitor>(vis: &mut T, fn_ret_ty: &mut FnRetTy) {
|
|||
|
||||
fn walk_param_bound<T: MutVisitor>(vis: &mut T, pb: &mut GenericBound) {
|
||||
match pb {
|
||||
GenericBound::Trait(ty, _modifier) => vis.visit_poly_trait_ref(ty),
|
||||
GenericBound::Trait(trait_ref) => vis.visit_poly_trait_ref(trait_ref),
|
||||
GenericBound::Outlives(lifetime) => walk_lifetime(vis, lifetime),
|
||||
GenericBound::Use(args, span) => {
|
||||
for arg in args {
|
||||
|
@ -1038,7 +1034,7 @@ fn walk_trait_ref<T: MutVisitor>(vis: &mut T, TraitRef { path, ref_id }: &mut Tr
|
|||
}
|
||||
|
||||
fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
|
||||
let PolyTraitRef { bound_generic_params, trait_ref, span } = p;
|
||||
let PolyTraitRef { bound_generic_params, modifiers: _, trait_ref, span } = p;
|
||||
bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
|
||||
vis.visit_trait_ref(trait_ref);
|
||||
vis.visit_span(span);
|
||||
|
|
|
@ -247,7 +247,9 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
|
|||
break (mac.args.delim == Delimiter::Brace).then_some(mac);
|
||||
}
|
||||
|
||||
ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => {
|
||||
ast::TyKind::Ptr(mut_ty)
|
||||
| ast::TyKind::Ref(_, mut_ty)
|
||||
| ast::TyKind::PinnedRef(_, mut_ty) => {
|
||||
ty = &mut_ty.ty;
|
||||
}
|
||||
|
||||
|
@ -263,7 +265,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
|
|||
|
||||
ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
|
||||
match bounds.last() {
|
||||
Some(ast::GenericBound::Trait(bound, _)) => {
|
||||
Some(ast::GenericBound::Trait(bound)) => {
|
||||
match path_return_type(&bound.trait_ref.path) {
|
||||
Some(trailing_ty) => ty = trailing_ty,
|
||||
None => break None,
|
||||
|
@ -287,12 +289,6 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
|
|||
| ast::TyKind::Pat(..)
|
||||
| ast::TyKind::Dummy
|
||||
| ast::TyKind::Err(..) => break None,
|
||||
|
||||
// These end in brace, but cannot occur in a let-else statement.
|
||||
// They are only parsed as fields of a data structure. For the
|
||||
// purpose of denying trailing braces in the expression of a
|
||||
// let-else, we can disregard these.
|
||||
ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -329,7 +329,7 @@ pub fn walk_poly_trait_ref<'a, V>(visitor: &mut V, trait_ref: &'a PolyTraitRef)
|
|||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
let PolyTraitRef { bound_generic_params, trait_ref, span: _ } = trait_ref;
|
||||
let PolyTraitRef { bound_generic_params, modifiers: _, trait_ref, span: _ } = trait_ref;
|
||||
walk_list!(visitor, visit_generic_param, bound_generic_params);
|
||||
visitor.visit_trait_ref(trait_ref)
|
||||
}
|
||||
|
@ -499,7 +499,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
|
|||
match kind {
|
||||
TyKind::Slice(ty) | TyKind::Paren(ty) => try_visit!(visitor.visit_ty(ty)),
|
||||
TyKind::Ptr(MutTy { ty, mutbl: _ }) => try_visit!(visitor.visit_ty(ty)),
|
||||
TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ }) => {
|
||||
TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ })
|
||||
| TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl: _ }) => {
|
||||
visit_opt!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
}
|
||||
|
@ -535,9 +536,6 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
|
|||
TyKind::Err(_guar) => {}
|
||||
TyKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
|
||||
TyKind::Never | TyKind::CVarArgs => {}
|
||||
TyKind::AnonStruct(_id, ref fields) | TyKind::AnonUnion(_id, ref fields) => {
|
||||
walk_list!(visitor, visit_field_def, fields);
|
||||
}
|
||||
}
|
||||
V::Result::output()
|
||||
}
|
||||
|
@ -723,7 +721,7 @@ impl WalkItemKind for ForeignItemKind {
|
|||
|
||||
pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) -> V::Result {
|
||||
match bound {
|
||||
GenericBound::Trait(typ, _modifier) => visitor.visit_poly_trait_ref(typ),
|
||||
GenericBound::Trait(trait_ref) => visitor.visit_poly_trait_ref(trait_ref),
|
||||
GenericBound::Outlives(lifetime) => visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound),
|
||||
GenericBound::Use(args, _span) => {
|
||||
walk_list!(visitor, visit_precise_capturing_arg, args);
|
||||
|
|
|
@ -79,3 +79,10 @@ impl Mutability {
|
|||
matches!(self, Self::Not)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
|
||||
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
|
||||
pub enum Pinnedness {
|
||||
Not,
|
||||
Pinned,
|
||||
}
|
||||
|
|
|
@ -640,7 +640,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.lower_span(span),
|
||||
Some(self.allow_gen_future.clone()),
|
||||
);
|
||||
let resume_ty = self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span);
|
||||
let resume_ty =
|
||||
self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span, None);
|
||||
let input_ty = hir::Ty {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::TyKind::Path(resume_ty),
|
||||
|
@ -2065,7 +2066,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
lang_item: hir::LangItem,
|
||||
name: Symbol,
|
||||
) -> hir::Expr<'hir> {
|
||||
let qpath = self.make_lang_item_qpath(lang_item, self.lower_span(span));
|
||||
let qpath = self.make_lang_item_qpath(lang_item, self.lower_span(span), None);
|
||||
let path = hir::ExprKind::Path(hir::QPath::TypeRelative(
|
||||
self.arena.alloc(self.ty(span, hir::TyKind::Path(qpath))),
|
||||
self.arena.alloc(hir::PathSegment::new(
|
||||
|
|
|
@ -1504,8 +1504,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
for bound in &bound_pred.bounds {
|
||||
if !matches!(
|
||||
*bound,
|
||||
GenericBound::Trait(_, TraitBoundModifiers {
|
||||
polarity: BoundPolarity::Maybe(_),
|
||||
GenericBound::Trait(PolyTraitRef {
|
||||
modifiers: TraitBoundModifiers { polarity: BoundPolarity::Maybe(_), .. },
|
||||
..
|
||||
})
|
||||
) {
|
||||
|
|
|
@ -55,8 +55,8 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
|
|||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalDefIdMap};
|
||||
use rustc_hir::{
|
||||
self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, MissingLifetimeKind, ParamName,
|
||||
TraitCandidate,
|
||||
self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, MissingLifetimeKind,
|
||||
ParamName, TraitCandidate,
|
||||
};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_macros::extension;
|
||||
|
@ -765,8 +765,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
res
|
||||
}
|
||||
|
||||
fn make_lang_item_qpath(&mut self, lang_item: hir::LangItem, span: Span) -> hir::QPath<'hir> {
|
||||
hir::QPath::Resolved(None, self.make_lang_item_path(lang_item, span, None))
|
||||
fn make_lang_item_qpath(
|
||||
&mut self,
|
||||
lang_item: hir::LangItem,
|
||||
span: Span,
|
||||
args: Option<&'hir hir::GenericArgs<'hir>>,
|
||||
) -> hir::QPath<'hir> {
|
||||
hir::QPath::Resolved(None, self.make_lang_item_path(lang_item, span, args))
|
||||
}
|
||||
|
||||
fn make_lang_item_path(
|
||||
|
@ -1219,13 +1224,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let bound = this.lower_poly_trait_ref(
|
||||
&PolyTraitRef {
|
||||
bound_generic_params: ThinVec::new(),
|
||||
modifiers: TraitBoundModifiers::NONE,
|
||||
trait_ref: TraitRef { path: path.clone(), ref_id: t.id },
|
||||
span: t.span,
|
||||
},
|
||||
itctx,
|
||||
TraitBoundModifiers::NONE,
|
||||
);
|
||||
let bound = (bound, hir::TraitBoundModifier::None);
|
||||
let bounds = this.arena.alloc_from_iter([bound]);
|
||||
let lifetime_bound = this.elided_dyn_bound(t.span);
|
||||
(bounds, lifetime_bound)
|
||||
|
@ -1259,46 +1263,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let kind = match &t.kind {
|
||||
TyKind::Infer => hir::TyKind::Infer,
|
||||
TyKind::Err(guar) => hir::TyKind::Err(*guar),
|
||||
// Lower the anonymous structs or unions in a nested lowering context.
|
||||
//
|
||||
// ```
|
||||
// struct Foo {
|
||||
// _: union {
|
||||
// // ^__________________ <-- within the nested lowering context,
|
||||
// /* fields */ // | we lower all fields defined into an
|
||||
// } // | owner node of struct or union item
|
||||
// // ^_____________________|
|
||||
// }
|
||||
// ```
|
||||
TyKind::AnonStruct(node_id, fields) | TyKind::AnonUnion(node_id, fields) => {
|
||||
// Here its `def_id` is created in `build_reduced_graph`.
|
||||
let def_id = self.local_def_id(*node_id);
|
||||
debug!(?def_id);
|
||||
let owner_id = hir::OwnerId { def_id };
|
||||
self.with_hir_id_owner(*node_id, |this| {
|
||||
let fields = this.arena.alloc_from_iter(
|
||||
fields.iter().enumerate().map(|f| this.lower_field_def(f)),
|
||||
);
|
||||
let span = t.span;
|
||||
let variant_data =
|
||||
hir::VariantData::Struct { fields, recovered: ast::Recovered::No };
|
||||
// FIXME: capture the generics from the outer adt.
|
||||
let generics = hir::Generics::empty();
|
||||
let kind = match t.kind {
|
||||
TyKind::AnonStruct(..) => hir::ItemKind::Struct(variant_data, generics),
|
||||
TyKind::AnonUnion(..) => hir::ItemKind::Union(variant_data, generics),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
hir::OwnerNode::Item(this.arena.alloc(hir::Item {
|
||||
ident: Ident::new(kw::Empty, span),
|
||||
owner_id,
|
||||
kind,
|
||||
span: this.lower_span(span),
|
||||
vis_span: this.lower_span(span.shrink_to_lo()),
|
||||
}))
|
||||
});
|
||||
hir::TyKind::AnonAdt(hir::ItemId { owner_id })
|
||||
}
|
||||
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
|
||||
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
|
||||
TyKind::Ref(region, mt) => {
|
||||
|
@ -1317,6 +1281,32 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let lifetime = self.lower_lifetime(®ion);
|
||||
hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx))
|
||||
}
|
||||
TyKind::PinnedRef(region, mt) => {
|
||||
let region = region.unwrap_or_else(|| {
|
||||
let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) =
|
||||
self.resolver.get_lifetime_res(t.id)
|
||||
{
|
||||
debug_assert_eq!(start.plus(1), end);
|
||||
start
|
||||
} else {
|
||||
self.next_node_id()
|
||||
};
|
||||
let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
|
||||
Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }
|
||||
});
|
||||
let lifetime = self.lower_lifetime(®ion);
|
||||
let kind = hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx));
|
||||
let span = self.lower_span(t.span);
|
||||
let arg = hir::Ty { kind, span, hir_id: self.next_id() };
|
||||
let args = self.arena.alloc(hir::GenericArgs {
|
||||
args: self.arena.alloc([hir::GenericArg::Type(self.arena.alloc(arg))]),
|
||||
constraints: &[],
|
||||
parenthesized: hir::GenericArgsParentheses::No,
|
||||
span_ext: span,
|
||||
});
|
||||
let path = self.make_lang_item_qpath(LangItem::Pin, span, Some(args));
|
||||
hir::TyKind::Path(path)
|
||||
}
|
||||
TyKind::BareFn(f) => {
|
||||
let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params);
|
||||
hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy {
|
||||
|
@ -1366,16 +1356,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// We can safely ignore constness here since AST validation
|
||||
// takes care of rejecting invalid modifier combinations and
|
||||
// const trait bounds in trait object types.
|
||||
GenericBound::Trait(ty, modifiers) => {
|
||||
// Still, don't pass along the constness here; we don't want to
|
||||
// synthesize any host effect args, it'd only cause problems.
|
||||
let modifiers = TraitBoundModifiers {
|
||||
constness: BoundConstness::Never,
|
||||
..*modifiers
|
||||
};
|
||||
let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers);
|
||||
let polarity = this.lower_trait_bound_modifiers(modifiers);
|
||||
Some((trait_ref, polarity))
|
||||
GenericBound::Trait(ty) => {
|
||||
let trait_ref = this.lower_poly_trait_ref(ty, itctx);
|
||||
Some(trait_ref)
|
||||
}
|
||||
GenericBound::Outlives(lifetime) => {
|
||||
if lifetime_bound.is_none() {
|
||||
|
@ -1573,11 +1556,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// Feature gate for RPITIT + use<..>
|
||||
match origin {
|
||||
rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => {
|
||||
if let Some(span) = bounds.iter().find_map(|bound| match *bound {
|
||||
ast::GenericBound::Use(_, span) => Some(span),
|
||||
_ => None,
|
||||
}) {
|
||||
self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnRpitit { span });
|
||||
if !self.tcx.features().precise_capturing_in_traits
|
||||
&& let Some(span) = bounds.iter().find_map(|bound| match *bound {
|
||||
ast::GenericBound::Use(_, span) => Some(span),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
let mut diag =
|
||||
self.tcx.dcx().create_err(errors::NoPreciseCapturesOnRpitit { span });
|
||||
add_feature_diagnostics(
|
||||
&mut diag,
|
||||
self.tcx.sess,
|
||||
sym::precise_capturing_in_traits,
|
||||
);
|
||||
diag.emit();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
@ -1882,10 +1874,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// Given we are only considering `ImplicitSelf` types, we needn't consider
|
||||
// the case where we have a mutable pattern to a reference as that would
|
||||
// no longer be an `ImplicitSelf`.
|
||||
TyKind::Ref(_, mt) if mt.ty.kind.is_implicit_self() => match mt.mutbl {
|
||||
hir::Mutability::Not => hir::ImplicitSelfKind::RefImm,
|
||||
hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut,
|
||||
},
|
||||
TyKind::Ref(_, mt) | TyKind::PinnedRef(_, mt)
|
||||
if mt.ty.kind.is_implicit_self() =>
|
||||
{
|
||||
match mt.mutbl {
|
||||
hir::Mutability::Not => hir::ImplicitSelfKind::RefImm,
|
||||
hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut,
|
||||
}
|
||||
}
|
||||
_ => hir::ImplicitSelfKind::None,
|
||||
}
|
||||
}),
|
||||
|
@ -1995,21 +1991,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
span_ext: DUMMY_SP,
|
||||
});
|
||||
|
||||
hir::GenericBound::Trait(
|
||||
hir::PolyTraitRef {
|
||||
bound_generic_params: &[],
|
||||
trait_ref: hir::TraitRef {
|
||||
path: self.make_lang_item_path(
|
||||
trait_lang_item,
|
||||
opaque_ty_span,
|
||||
Some(bound_args),
|
||||
),
|
||||
hir_ref_id: self.next_id(),
|
||||
},
|
||||
span: opaque_ty_span,
|
||||
hir::GenericBound::Trait(hir::PolyTraitRef {
|
||||
bound_generic_params: &[],
|
||||
modifiers: hir::TraitBoundModifier::None,
|
||||
trait_ref: hir::TraitRef {
|
||||
path: self.make_lang_item_path(trait_lang_item, opaque_ty_span, Some(bound_args)),
|
||||
hir_ref_id: self.next_id(),
|
||||
},
|
||||
hir::TraitBoundModifier::None,
|
||||
)
|
||||
span: opaque_ty_span,
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
|
@ -2019,10 +2009,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
itctx: ImplTraitContext,
|
||||
) -> hir::GenericBound<'hir> {
|
||||
match tpb {
|
||||
GenericBound::Trait(p, modifiers) => hir::GenericBound::Trait(
|
||||
self.lower_poly_trait_ref(p, itctx, *modifiers),
|
||||
self.lower_trait_bound_modifiers(*modifiers),
|
||||
),
|
||||
GenericBound::Trait(p) => hir::GenericBound::Trait(self.lower_poly_trait_ref(p, itctx)),
|
||||
GenericBound::Outlives(lifetime) => {
|
||||
hir::GenericBound::Outlives(self.lower_lifetime(lifetime))
|
||||
}
|
||||
|
@ -2226,12 +2213,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
&mut self,
|
||||
p: &PolyTraitRef,
|
||||
itctx: ImplTraitContext,
|
||||
modifiers: ast::TraitBoundModifiers,
|
||||
) -> hir::PolyTraitRef<'hir> {
|
||||
let bound_generic_params =
|
||||
self.lower_lifetime_binder(p.trait_ref.ref_id, &p.bound_generic_params);
|
||||
let trait_ref = self.lower_trait_ref(modifiers, &p.trait_ref, itctx);
|
||||
hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
|
||||
let trait_ref = self.lower_trait_ref(p.modifiers, &p.trait_ref, itctx);
|
||||
let modifiers = self.lower_trait_bound_modifiers(p.modifiers);
|
||||
hir::PolyTraitRef {
|
||||
bound_generic_params,
|
||||
modifiers,
|
||||
trait_ref,
|
||||
span: self.lower_span(p.span),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_mt(&mut self, mt: &MutTy, itctx: ImplTraitContext) -> hir::MutTy<'hir> {
|
||||
|
@ -2671,10 +2663,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => {
|
||||
let principal = hir::PolyTraitRef {
|
||||
bound_generic_params: &[],
|
||||
modifiers: hir::TraitBoundModifier::None,
|
||||
trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
|
||||
span: self.lower_span(span),
|
||||
};
|
||||
let principal = (principal, hir::TraitBoundModifier::None);
|
||||
|
||||
// The original ID is taken by the `PolyTraitRef`,
|
||||
// so the `Ty` itself needs a different one.
|
||||
|
|
|
@ -95,7 +95,7 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
|
|||
visit::walk_ty(self, t);
|
||||
self.current_binders.pop();
|
||||
}
|
||||
TyKind::Ref(None, _) => {
|
||||
TyKind::Ref(None, _) | TyKind::PinnedRef(None, _) => {
|
||||
self.record_elided_anchor(t.id, t.span);
|
||||
visit::walk_ty(self, t);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
ast_passes_anon_struct_or_union_not_allowed =
|
||||
anonymous {$struct_or_union}s are not allowed outside of unnamed struct or union fields
|
||||
.label = anonymous {$struct_or_union} declared here
|
||||
|
||||
ast_passes_assoc_const_without_body =
|
||||
associated constant in `impl` without body
|
||||
.suggestion = provide a definition for the constant
|
||||
|
@ -66,12 +62,12 @@ ast_passes_equality_in_where = equality constraints are not yet supported in `wh
|
|||
|
||||
ast_passes_extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
|
||||
|
||||
ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have qualifiers
|
||||
ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have `{$kw}` qualifier
|
||||
.label = in this `extern` block
|
||||
.suggestion = remove this qualifier
|
||||
.suggestion = remove the `{$kw}` qualifier
|
||||
|
||||
ast_passes_extern_invalid_safety = items in unadorned `extern` blocks cannot have safety qualifiers
|
||||
.suggestion = add unsafe to this `extern` block
|
||||
ast_passes_extern_invalid_safety = items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
|
||||
.suggestion = add `unsafe` to this `extern` block
|
||||
|
||||
ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers
|
||||
.label = in this `extern` block
|
||||
|
@ -160,14 +156,6 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
|
|||
.type = inherent impl for this type
|
||||
.only_trait = only trait implementations may be annotated with {$annotation}
|
||||
|
||||
ast_passes_invalid_unnamed_field =
|
||||
unnamed fields are not allowed outside of structs or unions
|
||||
.label = unnamed field declared here
|
||||
|
||||
ast_passes_invalid_unnamed_field_ty =
|
||||
unnamed fields can only have struct or union types
|
||||
.label = not a struct or union
|
||||
|
||||
ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot be declared with `safe` safety qualifier
|
||||
.suggestion = remove safe from this item
|
||||
|
||||
|
|
|
@ -244,9 +244,6 @@ impl<'a> AstValidator<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
TyKind::AnonStruct(_, ref fields) | TyKind::AnonUnion(_, ref fields) => {
|
||||
walk_list!(self, visit_struct_field_def, fields)
|
||||
}
|
||||
_ => visit::walk_ty(self, t),
|
||||
}
|
||||
}
|
||||
|
@ -255,7 +252,6 @@ impl<'a> AstValidator<'a> {
|
|||
if let Some(ident) = field.ident
|
||||
&& ident.name == kw::Underscore
|
||||
{
|
||||
self.check_unnamed_field_ty(&field.ty, ident.span);
|
||||
self.visit_vis(&field.vis);
|
||||
self.visit_ident(ident);
|
||||
self.visit_ty_common(&field.ty);
|
||||
|
@ -294,39 +290,6 @@ impl<'a> AstValidator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) {
|
||||
if matches!(
|
||||
&ty.kind,
|
||||
// We already checked for `kw::Underscore` before calling this function,
|
||||
// so skip the check
|
||||
TyKind::AnonStruct(..) | TyKind::AnonUnion(..)
|
||||
// If the anonymous field contains a Path as type, we can't determine
|
||||
// if the path is a valid struct or union, so skip the check
|
||||
| TyKind::Path(..)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
self.dcx().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
|
||||
}
|
||||
|
||||
fn deny_anon_struct_or_union(&self, ty: &Ty) {
|
||||
let struct_or_union = match &ty.kind {
|
||||
TyKind::AnonStruct(..) => "struct",
|
||||
TyKind::AnonUnion(..) => "union",
|
||||
_ => return,
|
||||
};
|
||||
self.dcx().emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
|
||||
}
|
||||
|
||||
fn deny_unnamed_field(&self, field: &FieldDef) {
|
||||
if let Some(ident) = field.ident
|
||||
&& ident.name == kw::Underscore
|
||||
{
|
||||
self.dcx()
|
||||
.emit_err(errors::InvalidUnnamedField { span: field.span, ident_span: ident.span });
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrTraitImpl) {
|
||||
let Const::Yes(span) = constness else {
|
||||
return;
|
||||
|
@ -561,21 +524,24 @@ impl<'a> AstValidator<'a> {
|
|||
// Deconstruct to ensure exhaustiveness
|
||||
FnHeader { safety: _, coroutine_kind, constness, ext }: FnHeader,
|
||||
) {
|
||||
let report_err = |span| {
|
||||
self.dcx()
|
||||
.emit_err(errors::FnQualifierInExtern { span, block: self.current_extern_span() });
|
||||
let report_err = |span, kw| {
|
||||
self.dcx().emit_err(errors::FnQualifierInExtern {
|
||||
span,
|
||||
kw,
|
||||
block: self.current_extern_span(),
|
||||
});
|
||||
};
|
||||
match coroutine_kind {
|
||||
Some(knd) => report_err(knd.span()),
|
||||
Some(kind) => report_err(kind.span(), kind.as_str()),
|
||||
None => (),
|
||||
}
|
||||
match constness {
|
||||
Const::Yes(span) => report_err(span),
|
||||
Const::Yes(span) => report_err(span, "const"),
|
||||
Const::No => (),
|
||||
}
|
||||
match ext {
|
||||
Extern::None => (),
|
||||
Extern::Implicit(span) | Extern::Explicit(_, span) => report_err(span),
|
||||
Extern::Implicit(span) | Extern::Explicit(_, span) => report_err(span, "extern"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -890,15 +856,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
|
||||
fn visit_ty(&mut self, ty: &'a Ty) {
|
||||
self.visit_ty_common(ty);
|
||||
self.deny_anon_struct_or_union(ty);
|
||||
self.walk_ty(ty)
|
||||
}
|
||||
|
||||
fn visit_field_def(&mut self, field: &'a FieldDef) {
|
||||
self.deny_unnamed_field(field);
|
||||
visit::walk_field_def(self, field)
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, item: &'a Item) {
|
||||
if item.attrs.iter().any(|attr| attr.is_proc_macro_attr()) {
|
||||
self.has_proc_macro_decls = true;
|
||||
|
@ -1303,7 +1263,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
if !bound_pred.bound_generic_params.is_empty() {
|
||||
for bound in &bound_pred.bounds {
|
||||
match bound {
|
||||
GenericBound::Trait(t, _) => {
|
||||
GenericBound::Trait(t) => {
|
||||
if !t.bound_generic_params.is_empty() {
|
||||
self.dcx()
|
||||
.emit_err(errors::NestedLifetimes { span: t.span });
|
||||
|
@ -1323,8 +1283,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
|
||||
fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
|
||||
match bound {
|
||||
GenericBound::Trait(trait_ref, modifiers) => {
|
||||
match (ctxt, modifiers.constness, modifiers.polarity) {
|
||||
GenericBound::Trait(trait_ref) => {
|
||||
match (ctxt, trait_ref.modifiers.constness, trait_ref.modifiers.polarity) {
|
||||
(BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
|
||||
if !self.features.more_maybe_bounds =>
|
||||
{
|
||||
|
@ -1364,7 +1324,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
}
|
||||
|
||||
// Negative trait bounds are not allowed to have associated constraints
|
||||
if let BoundPolarity::Negative(_) = modifiers.polarity
|
||||
if let BoundPolarity::Negative(_) = trait_ref.modifiers.polarity
|
||||
&& let Some(segment) = trait_ref.trait_ref.path.segments.last()
|
||||
{
|
||||
match segment.args.as_deref() {
|
||||
|
@ -1712,7 +1672,9 @@ fn deny_equality_constraints(
|
|||
}),
|
||||
) {
|
||||
for bound in bounds {
|
||||
if let GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
|
||||
if let GenericBound::Trait(poly) = bound
|
||||
&& poly.modifiers == TraitBoundModifiers::NONE
|
||||
{
|
||||
if full_path.segments[..full_path.segments.len() - 1]
|
||||
.iter()
|
||||
.map(|segment| segment.ident.name)
|
||||
|
@ -1740,7 +1702,9 @@ fn deny_equality_constraints(
|
|||
) {
|
||||
if ident == potential_param.ident {
|
||||
for bound in bounds {
|
||||
if let ast::GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
|
||||
if let ast::GenericBound::Trait(poly) = bound
|
||||
&& poly.modifiers == TraitBoundModifiers::NONE
|
||||
{
|
||||
suggest(poly, potential_assoc, predicate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -295,6 +295,7 @@ pub(crate) struct FnQualifierInExtern {
|
|||
pub span: Span,
|
||||
#[label]
|
||||
pub block: Span,
|
||||
pub kw: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
@ -814,33 +815,6 @@ pub(crate) struct NegativeBoundWithParentheticalNotation {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_invalid_unnamed_field_ty)]
|
||||
pub(crate) struct InvalidUnnamedFieldTy {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
pub ty_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_invalid_unnamed_field)]
|
||||
pub(crate) struct InvalidUnnamedField {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
pub ident_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_anon_struct_or_union_not_allowed)]
|
||||
pub(crate) struct AnonStructOrUnionNotAllowed {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub struct_or_union: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_match_arm_with_no_body)]
|
||||
pub(crate) struct MatchArmWithNoBody {
|
||||
|
|
|
@ -541,12 +541,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
|
||||
gate_all!(explicit_tail_calls, "`become` expression is experimental");
|
||||
gate_all!(generic_const_items, "generic const items are experimental");
|
||||
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
|
||||
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
|
||||
gate_all!(postfix_match, "postfix match is experimental");
|
||||
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
|
||||
gate_all!(global_registration, "global registration is experimental");
|
||||
gate_all!(return_type_notation, "return type notation is experimental");
|
||||
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
|
||||
|
||||
if !visitor.features.never_patterns {
|
||||
if let Some(spans) = spans.get(&sym::never_patterns) {
|
||||
|
@ -666,7 +666,7 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
|
|||
}
|
||||
|
||||
fn check_new_solver_banned_features(sess: &Session, features: &Features) {
|
||||
if !sess.opts.unstable_opts.next_solver.is_some_and(|n| n.globally) {
|
||||
if !sess.opts.unstable_opts.next_solver.globally {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ mod item;
|
|||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use ast::TraitBoundModifiers;
|
||||
use rustc_ast::attr::AttrIdGenerator;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{
|
||||
|
@ -1163,6 +1162,12 @@ impl<'a> State<'a> {
|
|||
self.print_opt_lifetime(lifetime);
|
||||
self.print_mt(mt, false);
|
||||
}
|
||||
ast::TyKind::PinnedRef(lifetime, mt) => {
|
||||
self.word("&");
|
||||
self.print_opt_lifetime(lifetime);
|
||||
self.word("pin ");
|
||||
self.print_mt(mt, true);
|
||||
}
|
||||
ast::TyKind::Never => {
|
||||
self.word("!");
|
||||
}
|
||||
|
@ -1174,14 +1179,6 @@ impl<'a> State<'a> {
|
|||
}
|
||||
self.pclose();
|
||||
}
|
||||
ast::TyKind::AnonStruct(_, fields) => {
|
||||
self.head("struct");
|
||||
self.print_record_struct_body(fields, ty.span);
|
||||
}
|
||||
ast::TyKind::AnonUnion(_, fields) => {
|
||||
self.head("union");
|
||||
self.print_record_struct_body(fields, ty.span);
|
||||
}
|
||||
ast::TyKind::Paren(typ) => {
|
||||
self.popen();
|
||||
self.print_type(typ);
|
||||
|
@ -1261,6 +1258,27 @@ impl<'a> State<'a> {
|
|||
|
||||
fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
|
||||
self.print_formal_generic_params(&t.bound_generic_params);
|
||||
|
||||
let ast::TraitBoundModifiers { constness, asyncness, polarity } = t.modifiers;
|
||||
match constness {
|
||||
ast::BoundConstness::Never => {}
|
||||
ast::BoundConstness::Always(_) | ast::BoundConstness::Maybe(_) => {
|
||||
self.word_space(constness.as_str());
|
||||
}
|
||||
}
|
||||
match asyncness {
|
||||
ast::BoundAsyncness::Normal => {}
|
||||
ast::BoundAsyncness::Async(_) => {
|
||||
self.word_space(asyncness.as_str());
|
||||
}
|
||||
}
|
||||
match polarity {
|
||||
ast::BoundPolarity::Positive => {}
|
||||
ast::BoundPolarity::Negative(_) | ast::BoundPolarity::Maybe(_) => {
|
||||
self.word(polarity.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
self.print_trait_ref(&t.trait_ref)
|
||||
}
|
||||
|
||||
|
@ -1748,31 +1766,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
match bound {
|
||||
GenericBound::Trait(
|
||||
tref,
|
||||
TraitBoundModifiers { constness, asyncness, polarity },
|
||||
) => {
|
||||
match constness {
|
||||
ast::BoundConstness::Never => {}
|
||||
ast::BoundConstness::Always(_) | ast::BoundConstness::Maybe(_) => {
|
||||
self.word_space(constness.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
match asyncness {
|
||||
ast::BoundAsyncness::Normal => {}
|
||||
ast::BoundAsyncness::Async(_) => {
|
||||
self.word_space(asyncness.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
match polarity {
|
||||
ast::BoundPolarity::Positive => {}
|
||||
ast::BoundPolarity::Negative(_) | ast::BoundPolarity::Maybe(_) => {
|
||||
self.word(polarity.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
GenericBound::Trait(tref) => {
|
||||
self.print_poly_trait_ref(tref);
|
||||
}
|
||||
GenericBound::Outlives(lt) => self.print_lifetime(*lt),
|
||||
|
|
|
@ -723,7 +723,13 @@ pub fn eval_condition(
|
|||
}
|
||||
|
||||
mis.iter().fold(true, |res, mi| {
|
||||
let mut mi = mi.meta_item().unwrap().clone();
|
||||
let Some(mut mi) = mi.meta_item().cloned() else {
|
||||
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
|
||||
span: mi.span(),
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
if let [seg, ..] = &mut mi.path.segments[..] {
|
||||
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::graph;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::{
|
||||
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
|
||||
};
|
||||
use rustc_middle::mir::{self, BasicBlock, Body, Location, Place, TerminatorEdges};
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
use rustc_mir_dataflow::fmt::DebugWithContext;
|
||||
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
|
||||
use rustc_mir_dataflow::{Analysis, AnalysisDomain, Forward, GenKill, Results, ResultsVisitable};
|
||||
use rustc_mir_dataflow::{Analysis, Forward, GenKill, Results, ResultsVisitable};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
|
||||
|
@ -22,9 +20,9 @@ pub(crate) struct BorrowckResults<'a, 'tcx> {
|
|||
/// The transient state of the dataflow analyses used by the borrow checker.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct BorrowckDomain<'a, 'tcx> {
|
||||
pub(crate) borrows: <Borrows<'a, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) borrows: <Borrows<'a, 'tcx> as Analysis<'tcx>>::Domain,
|
||||
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
|
||||
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'tcx> {
|
||||
|
@ -427,7 +425,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
|||
/// That means they went out of a nonlexical scope
|
||||
fn kill_loans_out_of_scope_at_location(
|
||||
&self,
|
||||
trans: &mut impl GenKill<BorrowIndex>,
|
||||
trans: &mut <Self as Analysis<'tcx>>::Domain,
|
||||
location: Location,
|
||||
) {
|
||||
// NOTE: The state associated with a given `location`
|
||||
|
@ -447,7 +445,11 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Kill any borrows that conflict with `place`.
|
||||
fn kill_borrows_on_place(&self, trans: &mut impl GenKill<BorrowIndex>, place: Place<'tcx>) {
|
||||
fn kill_borrows_on_place(
|
||||
&self,
|
||||
trans: &mut <Self as Analysis<'tcx>>::Domain,
|
||||
place: Place<'tcx>,
|
||||
) {
|
||||
debug!("kill_borrows_on_place: place={:?}", place);
|
||||
|
||||
let other_borrows_of_local = self
|
||||
|
@ -486,7 +488,14 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
|
||||
/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
|
||||
/// - we gen the introduced loans
|
||||
/// - we kill loans on locals going out of (regular) scope
|
||||
/// - we kill the loans going out of their region's NLL scope: in NLL terms, the frontier where a
|
||||
/// region stops containing the CFG points reachable from the issuing location.
|
||||
/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
|
||||
/// `a.b.c` when `a` is overwritten.
|
||||
impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
|
||||
type Domain = BitSet<BorrowIndex>;
|
||||
|
||||
const NAME: &'static str = "borrows";
|
||||
|
@ -500,34 +509,19 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
|
|||
// no borrows of code region_scopes have been taken prior to
|
||||
// function execution, so this method has no effect.
|
||||
}
|
||||
}
|
||||
|
||||
/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
|
||||
/// - we gen the introduced loans
|
||||
/// - we kill loans on locals going out of (regular) scope
|
||||
/// - we kill the loans going out of their region's NLL scope: in NLL terms, the frontier where a
|
||||
/// region stops containing the CFG points reachable from the issuing location.
|
||||
/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
|
||||
/// `a.b.c` when `a` is overwritten.
|
||||
impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
||||
type Idx = BorrowIndex;
|
||||
|
||||
fn domain_size(&self, _: &mir::Body<'tcx>) -> usize {
|
||||
self.borrow_set.len()
|
||||
}
|
||||
|
||||
fn before_statement_effect(
|
||||
fn apply_before_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut impl GenKill<Self::Idx>,
|
||||
trans: &mut Self::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
self.kill_loans_out_of_scope_at_location(trans, location);
|
||||
}
|
||||
|
||||
fn statement_effect(
|
||||
fn apply_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut impl GenKill<Self::Idx>,
|
||||
trans: &mut Self::Domain,
|
||||
stmt: &mir::Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
|
@ -573,7 +567,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn before_terminator_effect(
|
||||
fn apply_before_terminator_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
|
@ -582,7 +576,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
|||
self.kill_loans_out_of_scope_at_location(trans, location);
|
||||
}
|
||||
|
||||
fn terminator_effect<'mir>(
|
||||
fn apply_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
terminator: &'mir mir::Terminator<'tcx>,
|
||||
|
@ -599,14 +593,6 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
|||
}
|
||||
terminator.edges()
|
||||
}
|
||||
|
||||
fn call_return_effect(
|
||||
&mut self,
|
||||
_trans: &mut Self::Domain,
|
||||
_block: mir::BasicBlock,
|
||||
_return_places: CallReturnPlaces<'_, 'tcx>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> DebugWithContext<C> for BorrowIndex {}
|
||||
|
|
|
@ -706,7 +706,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
suggestions.push((
|
||||
pat_span,
|
||||
format!("consider removing the {to_remove}"),
|
||||
suggestion.to_string(),
|
||||
suggestion,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -254,7 +254,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
debug!(?hrtb_bounds);
|
||||
|
||||
hrtb_bounds.iter().for_each(|bound| {
|
||||
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else {
|
||||
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
|
||||
return;
|
||||
};
|
||||
diag.span_note(*trait_span, fluent::borrowck_limitations_implies_static);
|
||||
|
@ -277,7 +277,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
return;
|
||||
};
|
||||
bounds.iter().for_each(|bd| {
|
||||
if let Trait(PolyTraitRef { trait_ref: tr_ref, .. }, _) = bd
|
||||
if let Trait(PolyTraitRef { trait_ref: tr_ref, .. }) = bd
|
||||
&& let Def(_, res_defid) = tr_ref.path.res
|
||||
&& res_defid == trait_res_defid // trait id matches
|
||||
&& let TyKind::Path(Resolved(_, path)) = bounded_ty.kind
|
||||
|
|
|
@ -837,7 +837,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
hir_ty
|
||||
);
|
||||
};
|
||||
if let hir::OpaqueTy { bounds: [hir::GenericBound::Trait(trait_ref, _)], .. } = opaque_ty
|
||||
if let hir::OpaqueTy { bounds: [hir::GenericBound::Trait(trait_ref)], .. } = opaque_ty
|
||||
&& let Some(segment) = trait_ref.trait_ref.path.segments.last()
|
||||
&& let Some(args) = segment.args
|
||||
&& let [constraint] = args.constraints
|
||||
|
|
|
@ -165,7 +165,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
universe_causes,
|
||||
type_tests,
|
||||
liveness_constraints,
|
||||
elements.clone(),
|
||||
elements,
|
||||
);
|
||||
|
||||
// If requested: dump NLL facts, and run legacy polonius analysis.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//! we can prove overlap one way or another. Essentially, we treat `Overlap` as
|
||||
//! a monoid and report a conflict if the product ends up not being `Disjoint`.
|
||||
//!
|
||||
//! At each step, if we didn't run out of borrow or place, we know that our elements
|
||||
//! On each step, if we didn't run out of borrow or place, we know that our elements
|
||||
//! have the same type, and that they only overlap if they are the identical.
|
||||
//!
|
||||
//! For example, if we are comparing these:
|
||||
|
|
|
@ -18,6 +18,7 @@ use rustc_infer::infer::region_constraints::RegionConstraintData;
|
|||
use rustc_infer::infer::{
|
||||
BoundRegion, BoundRegionConversionTime, InferCtxt, NllRegionVariableOrigin,
|
||||
};
|
||||
use rustc_infer::traits::PredicateObligations;
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
|
@ -40,7 +41,6 @@ use rustc_span::source_map::Spanned;
|
|||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
use rustc_target::abi::{FIRST_VARIANT, FieldIdx};
|
||||
use rustc_trait_selection::traits::PredicateObligation;
|
||||
use rustc_trait_selection::traits::query::type_op::custom::{
|
||||
CustomTypeOp, scrape_region_constraints,
|
||||
};
|
||||
|
@ -2940,7 +2940,7 @@ impl NormalizeLocation for Location {
|
|||
pub(super) struct InstantiateOpaqueType<'tcx> {
|
||||
pub base_universe: Option<ty::UniverseIndex>,
|
||||
pub region_constraints: Option<RegionConstraintData<'tcx>>,
|
||||
pub obligations: Vec<PredicateObligation<'tcx>>,
|
||||
pub obligations: PredicateObligations<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> {
|
||||
|
|
|
@ -11,6 +11,7 @@ use rustc_middle::span_bug;
|
|||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::ty::fold::FnMutDelegate;
|
||||
use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
@ -362,7 +363,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
|
|||
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
|
||||
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
|
||||
) if a_def_id == b_def_id || infcx.next_trait_solver() => {
|
||||
infcx.super_combine_tys(self, a, b).map(|_| ()).or_else(|err| {
|
||||
super_combine_tys(&infcx.infcx, self, a, b).map(|_| ()).or_else(|err| {
|
||||
// This behavior is only there for the old solver, the new solver
|
||||
// shouldn't ever fail. Instead, it unconditionally emits an
|
||||
// alias-relate goal.
|
||||
|
@ -385,7 +386,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
|
|||
debug!(?a, ?b, ?self.ambient_variance);
|
||||
|
||||
// Will also handle unification of `IntVar` and `FloatVar`.
|
||||
self.type_checker.infcx.super_combine_tys(self, a, b)?;
|
||||
super_combine_tys(&self.type_checker.infcx.infcx, self, a, b)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -422,7 +423,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
|
|||
assert!(!a.has_non_region_infer(), "unexpected inference var {:?}", a);
|
||||
assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b);
|
||||
|
||||
self.type_checker.infcx.super_combine_consts(self, a, b)
|
||||
super_combine_consts(&self.type_checker.infcx.infcx, self, a, b)
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "trace")]
|
||||
|
|
|
@ -3,6 +3,10 @@ name = "rustc_builtin_macros"
|
|||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(llvm_enzyme)'] }
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
|
|
|
@ -69,6 +69,15 @@ builtin_macros_assert_requires_boolean = macro requires a boolean expression as
|
|||
builtin_macros_assert_requires_expression = macro requires an expression as an argument
|
||||
.suggestion = try removing semicolon
|
||||
|
||||
builtin_macros_autodiff = autodiff must be applied to function
|
||||
builtin_macros_autodiff_missing_config = autodiff requires at least a name and mode
|
||||
builtin_macros_autodiff_mode = unknown Mode: `{$mode}`. Use `Forward` or `Reverse`
|
||||
builtin_macros_autodiff_mode_activity = {$act} can not be used in {$mode} Mode
|
||||
builtin_macros_autodiff_not_build = this rustc version does not support autodiff
|
||||
builtin_macros_autodiff_number_activities = expected {$expected} activities, but found {$found}
|
||||
builtin_macros_autodiff_ty_activity = {$act} can not be used for this type
|
||||
|
||||
builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}`
|
||||
builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s
|
||||
.label = not applicable here
|
||||
.label2 = not a `struct`, `enum` or `union`
|
||||
|
|
820
compiler/rustc_builtin_macros/src/autodiff.rs
Normal file
820
compiler/rustc_builtin_macros/src/autodiff.rs
Normal file
|
@ -0,0 +1,820 @@
|
|||
//! This module contains the implementation of the `#[autodiff]` attribute.
|
||||
//! Currently our linter isn't smart enough to see that each import is used in one of the two
|
||||
//! configs (autodiff enabled or disabled), so we have to add cfg's to each import.
|
||||
//! FIXME(ZuseZ4): Remove this once we have a smarter linter.
|
||||
|
||||
#[cfg(llvm_enzyme)]
|
||||
mod llvm_enzyme {
|
||||
use std::str::FromStr;
|
||||
use std::string::String;
|
||||
|
||||
use rustc_ast::expand::autodiff_attrs::{
|
||||
AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ty_for_activity,
|
||||
};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{Token, TokenKind};
|
||||
use rustc_ast::tokenstream::*;
|
||||
use rustc_ast::visit::AssocCtxt::*;
|
||||
use rustc_ast::{
|
||||
self as ast, AssocItemKind, BindingMode, FnRetTy, FnSig, Generics, ItemKind, MetaItemInner,
|
||||
PatKind, TyKind,
|
||||
};
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_span::symbol::{Ident, kw, sym};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use thin_vec::{ThinVec, thin_vec};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::errors;
|
||||
|
||||
// If we have a default `()` return type or explicitley `()` return type,
|
||||
// then we often can skip doing some work.
|
||||
fn has_ret(ty: &FnRetTy) -> bool {
|
||||
match ty {
|
||||
FnRetTy::Ty(ty) => !ty.kind.is_unit(),
|
||||
FnRetTy::Default(_) => false,
|
||||
}
|
||||
}
|
||||
fn first_ident(x: &MetaItemInner) -> rustc_span::symbol::Ident {
|
||||
let segments = &x.meta_item().unwrap().path.segments;
|
||||
assert!(segments.len() == 1);
|
||||
segments[0].ident
|
||||
}
|
||||
|
||||
fn name(x: &MetaItemInner) -> String {
|
||||
first_ident(x).name.to_string()
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
meta_item: &ThinVec<MetaItemInner>,
|
||||
has_ret: bool,
|
||||
) -> AutoDiffAttrs {
|
||||
let dcx = ecx.sess.dcx();
|
||||
let mode = name(&meta_item[1]);
|
||||
let Ok(mode) = DiffMode::from_str(&mode) else {
|
||||
dcx.emit_err(errors::AutoDiffInvalidMode { span: meta_item[1].span(), mode });
|
||||
return AutoDiffAttrs::error();
|
||||
};
|
||||
let mut activities: Vec<DiffActivity> = vec![];
|
||||
let mut errors = false;
|
||||
for x in &meta_item[2..] {
|
||||
let activity_str = name(&x);
|
||||
let res = DiffActivity::from_str(&activity_str);
|
||||
match res {
|
||||
Ok(x) => activities.push(x),
|
||||
Err(_) => {
|
||||
dcx.emit_err(errors::AutoDiffUnknownActivity {
|
||||
span: x.span(),
|
||||
act: activity_str,
|
||||
});
|
||||
errors = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
if errors {
|
||||
return AutoDiffAttrs::error();
|
||||
}
|
||||
|
||||
// If a return type exist, we need to split the last activity,
|
||||
// otherwise we return None as placeholder.
|
||||
let (ret_activity, input_activity) = if has_ret {
|
||||
let Some((last, rest)) = activities.split_last() else {
|
||||
unreachable!(
|
||||
"should not be reachable because we counted the number of activities previously"
|
||||
);
|
||||
};
|
||||
(last, rest)
|
||||
} else {
|
||||
(&DiffActivity::None, activities.as_slice())
|
||||
};
|
||||
|
||||
AutoDiffAttrs { mode, ret_activity: *ret_activity, input_activity: input_activity.to_vec() }
|
||||
}
|
||||
|
||||
/// We expand the autodiff macro to generate a new placeholder function which passes
|
||||
/// type-checking and can be called by users. The function body of the placeholder function will
|
||||
/// later be replaced on LLVM-IR level, so the design of the body is less important and for now
|
||||
/// should just prevent early inlining and optimizations which alter the function signature.
|
||||
/// The exact signature of the generated function depends on the configuration provided by the
|
||||
/// user, but here is an example:
|
||||
///
|
||||
/// ```
|
||||
/// #[autodiff(cos_box, Reverse, Duplicated, Active)]
|
||||
/// fn sin(x: &Box<f32>) -> f32 {
|
||||
/// f32::sin(**x)
|
||||
/// }
|
||||
/// ```
|
||||
/// which becomes expanded to:
|
||||
/// ```
|
||||
/// #[rustc_autodiff]
|
||||
/// #[inline(never)]
|
||||
/// fn sin(x: &Box<f32>) -> f32 {
|
||||
/// f32::sin(**x)
|
||||
/// }
|
||||
/// #[rustc_autodiff(Reverse, Duplicated, Active)]
|
||||
/// #[inline(never)]
|
||||
/// fn cos_box(x: &Box<f32>, dx: &mut Box<f32>, dret: f32) -> f32 {
|
||||
/// unsafe {
|
||||
/// asm!("NOP");
|
||||
/// };
|
||||
/// ::core::hint::black_box(sin(x));
|
||||
/// ::core::hint::black_box((dx, dret));
|
||||
/// ::core::hint::black_box(sin(x))
|
||||
/// }
|
||||
/// ```
|
||||
/// FIXME(ZuseZ4): Once autodiff is enabled by default, make this a doc comment which is checked
|
||||
/// in CI.
|
||||
pub(crate) fn expand(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
expand_span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
mut item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
let dcx = ecx.sess.dcx();
|
||||
// first get the annotable item:
|
||||
let (sig, is_impl): (FnSig, bool) = match &item {
|
||||
Annotatable::Item(ref iitem) => {
|
||||
let sig = match &iitem.kind {
|
||||
ItemKind::Fn(box ast::Fn { sig, .. }) => sig,
|
||||
_ => {
|
||||
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
|
||||
return vec![item];
|
||||
}
|
||||
};
|
||||
(sig.clone(), false)
|
||||
}
|
||||
Annotatable::AssocItem(ref assoc_item, _) => {
|
||||
let sig = match &assoc_item.kind {
|
||||
ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) => sig,
|
||||
_ => {
|
||||
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
|
||||
return vec![item];
|
||||
}
|
||||
};
|
||||
(sig.clone(), true)
|
||||
}
|
||||
_ => {
|
||||
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
|
||||
return vec![item];
|
||||
}
|
||||
};
|
||||
|
||||
let meta_item_vec: ThinVec<MetaItemInner> = match meta_item.kind {
|
||||
ast::MetaItemKind::List(ref vec) => vec.clone(),
|
||||
_ => {
|
||||
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
|
||||
return vec![item];
|
||||
}
|
||||
};
|
||||
|
||||
let has_ret = has_ret(&sig.decl.output);
|
||||
let sig_span = ecx.with_call_site_ctxt(sig.span);
|
||||
|
||||
let (vis, primal) = match &item {
|
||||
Annotatable::Item(ref iitem) => (iitem.vis.clone(), iitem.ident.clone()),
|
||||
Annotatable::AssocItem(ref assoc_item, _) => {
|
||||
(assoc_item.vis.clone(), assoc_item.ident.clone())
|
||||
}
|
||||
_ => {
|
||||
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
|
||||
return vec![item];
|
||||
}
|
||||
};
|
||||
|
||||
// create TokenStream from vec elemtents:
|
||||
// meta_item doesn't have a .tokens field
|
||||
let comma: Token = Token::new(TokenKind::Comma, Span::default());
|
||||
let mut ts: Vec<TokenTree> = vec![];
|
||||
if meta_item_vec.len() < 2 {
|
||||
// At the bare minimum, we need a fnc name and a mode, even for a dummy function with no
|
||||
// input and output args.
|
||||
dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() });
|
||||
return vec![item];
|
||||
} else {
|
||||
for t in meta_item_vec.clone()[1..].iter() {
|
||||
let val = first_ident(t);
|
||||
let t = Token::from_ast_ident(val);
|
||||
ts.push(TokenTree::Token(t, Spacing::Joint));
|
||||
ts.push(TokenTree::Token(comma.clone(), Spacing::Alone));
|
||||
}
|
||||
}
|
||||
if !has_ret {
|
||||
// We don't want users to provide a return activity if the function doesn't return anything.
|
||||
// For simplicity, we just add a dummy token to the end of the list.
|
||||
let t = Token::new(TokenKind::Ident(sym::None, false.into()), Span::default());
|
||||
ts.push(TokenTree::Token(t, Spacing::Joint));
|
||||
}
|
||||
let ts: TokenStream = TokenStream::from_iter(ts);
|
||||
|
||||
let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret);
|
||||
if !x.is_active() {
|
||||
// We encountered an error, so we return the original item.
|
||||
// This allows us to potentially parse other attributes.
|
||||
return vec![item];
|
||||
}
|
||||
let span = ecx.with_def_site_ctxt(expand_span);
|
||||
|
||||
let n_active: u32 = x
|
||||
.input_activity
|
||||
.iter()
|
||||
.filter(|a| **a == DiffActivity::Active || **a == DiffActivity::ActiveOnly)
|
||||
.count() as u32;
|
||||
let (d_sig, new_args, idents, errored) = gen_enzyme_decl(ecx, &sig, &x, span);
|
||||
let new_decl_span = d_sig.span;
|
||||
let d_body = gen_enzyme_body(
|
||||
ecx,
|
||||
&x,
|
||||
n_active,
|
||||
&sig,
|
||||
&d_sig,
|
||||
primal,
|
||||
&new_args,
|
||||
span,
|
||||
sig_span,
|
||||
new_decl_span,
|
||||
idents,
|
||||
errored,
|
||||
);
|
||||
let d_ident = first_ident(&meta_item_vec[0]);
|
||||
|
||||
// The first element of it is the name of the function to be generated
|
||||
let asdf = Box::new(ast::Fn {
|
||||
defaultness: ast::Defaultness::Final,
|
||||
sig: d_sig,
|
||||
generics: Generics::default(),
|
||||
body: Some(d_body),
|
||||
});
|
||||
let mut rustc_ad_attr =
|
||||
P(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff)));
|
||||
|
||||
let ts2: Vec<TokenTree> = vec![TokenTree::Token(
|
||||
Token::new(TokenKind::Ident(sym::never, false.into()), span),
|
||||
Spacing::Joint,
|
||||
)];
|
||||
let never_arg = ast::DelimArgs {
|
||||
dspan: ast::tokenstream::DelimSpan::from_single(span),
|
||||
delim: ast::token::Delimiter::Parenthesis,
|
||||
tokens: ast::tokenstream::TokenStream::from_iter(ts2),
|
||||
};
|
||||
let inline_item = ast::AttrItem {
|
||||
unsafety: ast::Safety::Default,
|
||||
path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)),
|
||||
args: ast::AttrArgs::Delimited(never_arg),
|
||||
tokens: None,
|
||||
};
|
||||
let inline_never_attr = P(ast::NormalAttr { item: inline_item, tokens: None });
|
||||
let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
|
||||
let attr: ast::Attribute = ast::Attribute {
|
||||
kind: ast::AttrKind::Normal(rustc_ad_attr.clone()),
|
||||
id: new_id,
|
||||
style: ast::AttrStyle::Outer,
|
||||
span,
|
||||
};
|
||||
let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
|
||||
let inline_never: ast::Attribute = ast::Attribute {
|
||||
kind: ast::AttrKind::Normal(inline_never_attr),
|
||||
id: new_id,
|
||||
style: ast::AttrStyle::Outer,
|
||||
span,
|
||||
};
|
||||
|
||||
// Don't add it multiple times:
|
||||
let orig_annotatable: Annotatable = match item {
|
||||
Annotatable::Item(ref mut iitem) => {
|
||||
if !iitem.attrs.iter().any(|a| a.id == attr.id) {
|
||||
iitem.attrs.push(attr.clone());
|
||||
}
|
||||
if !iitem.attrs.iter().any(|a| a.id == inline_never.id) {
|
||||
iitem.attrs.push(inline_never.clone());
|
||||
}
|
||||
Annotatable::Item(iitem.clone())
|
||||
}
|
||||
Annotatable::AssocItem(ref mut assoc_item, i @ Impl) => {
|
||||
if !assoc_item.attrs.iter().any(|a| a.id == attr.id) {
|
||||
assoc_item.attrs.push(attr.clone());
|
||||
}
|
||||
if !assoc_item.attrs.iter().any(|a| a.id == inline_never.id) {
|
||||
assoc_item.attrs.push(inline_never.clone());
|
||||
}
|
||||
Annotatable::AssocItem(assoc_item.clone(), i)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("annotatable kind checked previously")
|
||||
}
|
||||
};
|
||||
// Now update for d_fn
|
||||
rustc_ad_attr.item.args = rustc_ast::AttrArgs::Delimited(rustc_ast::DelimArgs {
|
||||
dspan: DelimSpan::dummy(),
|
||||
delim: rustc_ast::token::Delimiter::Parenthesis,
|
||||
tokens: ts,
|
||||
});
|
||||
let d_attr: ast::Attribute = ast::Attribute {
|
||||
kind: ast::AttrKind::Normal(rustc_ad_attr.clone()),
|
||||
id: new_id,
|
||||
style: ast::AttrStyle::Outer,
|
||||
span,
|
||||
};
|
||||
|
||||
let d_annotatable = if is_impl {
|
||||
let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf);
|
||||
let d_fn = P(ast::AssocItem {
|
||||
attrs: thin_vec![d_attr.clone(), inline_never],
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span,
|
||||
vis,
|
||||
ident: d_ident,
|
||||
kind: assoc_item,
|
||||
tokens: None,
|
||||
});
|
||||
Annotatable::AssocItem(d_fn, Impl)
|
||||
} else {
|
||||
let mut d_fn = ecx.item(
|
||||
span,
|
||||
d_ident,
|
||||
thin_vec![d_attr.clone(), inline_never],
|
||||
ItemKind::Fn(asdf),
|
||||
);
|
||||
d_fn.vis = vis;
|
||||
Annotatable::Item(d_fn)
|
||||
};
|
||||
|
||||
return vec![orig_annotatable, d_annotatable];
|
||||
}
|
||||
|
||||
// shadow arguments (the extra ones which were not in the original (primal) function), in reverse mode must be
|
||||
// mutable references or ptrs, because Enzyme will write into them.
|
||||
fn assure_mut_ref(ty: &ast::Ty) -> ast::Ty {
|
||||
let mut ty = ty.clone();
|
||||
match ty.kind {
|
||||
TyKind::Ptr(ref mut mut_ty) => {
|
||||
mut_ty.mutbl = ast::Mutability::Mut;
|
||||
}
|
||||
TyKind::Ref(_, ref mut mut_ty) => {
|
||||
mut_ty.mutbl = ast::Mutability::Mut;
|
||||
}
|
||||
_ => {
|
||||
panic!("unsupported type: {:?}", ty);
|
||||
}
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
||||
/// We only want this function to type-check, since we will replace the body
|
||||
/// later on llvm level. Using `loop {}` does not cover all return types anymore,
|
||||
/// so instead we build something that should pass. We also add a inline_asm
|
||||
/// line, as one more barrier for rustc to prevent inlining of this function.
|
||||
/// FIXME(ZuseZ4): We still have cases of incorrect inlining across modules, see
|
||||
/// <https://github.com/EnzymeAD/rust/issues/173>, so this isn't sufficient.
|
||||
/// It also triggers an Enzyme crash if we due to a bug ever try to differentiate
|
||||
/// this function (which should never happen, since it is only a placeholder).
|
||||
/// Finally, we also add back_box usages of all input arguments, to prevent rustc
|
||||
/// from optimizing any arguments away.
|
||||
fn gen_enzyme_body(
|
||||
ecx: &ExtCtxt<'_>,
|
||||
x: &AutoDiffAttrs,
|
||||
n_active: u32,
|
||||
sig: &ast::FnSig,
|
||||
d_sig: &ast::FnSig,
|
||||
primal: Ident,
|
||||
new_names: &[String],
|
||||
span: Span,
|
||||
sig_span: Span,
|
||||
new_decl_span: Span,
|
||||
idents: Vec<Ident>,
|
||||
errored: bool,
|
||||
) -> P<ast::Block> {
|
||||
let blackbox_path = ecx.std_path(&[sym::hint, sym::black_box]);
|
||||
let noop = ast::InlineAsm {
|
||||
asm_macro: ast::AsmMacro::Asm,
|
||||
template: vec![ast::InlineAsmTemplatePiece::String("NOP".into())],
|
||||
template_strs: Box::new([]),
|
||||
operands: vec![],
|
||||
clobber_abis: vec![],
|
||||
options: ast::InlineAsmOptions::PURE | ast::InlineAsmOptions::NOMEM,
|
||||
line_spans: vec![],
|
||||
};
|
||||
let noop_expr = ecx.expr_asm(span, P(noop));
|
||||
let unsf = ast::BlockCheckMode::Unsafe(ast::UnsafeSource::CompilerGenerated);
|
||||
let unsf_block = ast::Block {
|
||||
stmts: thin_vec![ecx.stmt_semi(noop_expr)],
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
tokens: None,
|
||||
rules: unsf,
|
||||
span,
|
||||
could_be_bare_literal: false,
|
||||
};
|
||||
let unsf_expr = ecx.expr_block(P(unsf_block));
|
||||
let blackbox_call_expr = ecx.expr_path(ecx.path(span, blackbox_path));
|
||||
let primal_call = gen_primal_call(ecx, span, primal, idents);
|
||||
let black_box_primal_call =
|
||||
ecx.expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![
|
||||
primal_call.clone()
|
||||
]);
|
||||
let tup_args = new_names
|
||||
.iter()
|
||||
.map(|arg| ecx.expr_path(ecx.path_ident(span, Ident::from_str(arg))))
|
||||
.collect();
|
||||
|
||||
let black_box_remaining_args =
|
||||
ecx.expr_call(sig_span, blackbox_call_expr.clone(), thin_vec![
|
||||
ecx.expr_tuple(sig_span, tup_args)
|
||||
]);
|
||||
|
||||
let mut body = ecx.block(span, ThinVec::new());
|
||||
body.stmts.push(ecx.stmt_semi(unsf_expr));
|
||||
|
||||
// This uses primal args which won't be available if we errored before
|
||||
if !errored {
|
||||
body.stmts.push(ecx.stmt_semi(black_box_primal_call.clone()));
|
||||
}
|
||||
body.stmts.push(ecx.stmt_semi(black_box_remaining_args));
|
||||
|
||||
if !has_ret(&d_sig.decl.output) {
|
||||
// there is no return type that we have to match, () works fine.
|
||||
return body;
|
||||
}
|
||||
|
||||
// having an active-only return means we'll drop the original return type.
|
||||
// So that can be treated identical to not having one in the first place.
|
||||
let primal_ret = has_ret(&sig.decl.output) && !x.has_active_only_ret();
|
||||
|
||||
if primal_ret && n_active == 0 && x.mode.is_rev() {
|
||||
// We only have the primal ret.
|
||||
body.stmts.push(ecx.stmt_expr(black_box_primal_call.clone()));
|
||||
return body;
|
||||
}
|
||||
|
||||
if !primal_ret && n_active == 1 {
|
||||
// Again no tuple return, so return default float val.
|
||||
let ty = match d_sig.decl.output {
|
||||
FnRetTy::Ty(ref ty) => ty.clone(),
|
||||
FnRetTy::Default(span) => {
|
||||
panic!("Did not expect Default ret ty: {:?}", span);
|
||||
}
|
||||
};
|
||||
let arg = ty.kind.is_simple_path().unwrap();
|
||||
let sl: Vec<Symbol> = vec![arg, kw::Default];
|
||||
let tmp = ecx.def_site_path(&sl);
|
||||
let default_call_expr = ecx.expr_path(ecx.path(span, tmp));
|
||||
let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
|
||||
body.stmts.push(ecx.stmt_expr(default_call_expr));
|
||||
return body;
|
||||
}
|
||||
|
||||
let mut exprs = ThinVec::<P<ast::Expr>>::new();
|
||||
if primal_ret {
|
||||
// We have both primal ret and active floats.
|
||||
// primal ret is first, by construction.
|
||||
exprs.push(primal_call.clone());
|
||||
}
|
||||
|
||||
// Now construct default placeholder for each active float.
|
||||
// Is there something nicer than f32::default() and f64::default()?
|
||||
let d_ret_ty = match d_sig.decl.output {
|
||||
FnRetTy::Ty(ref ty) => ty.clone(),
|
||||
FnRetTy::Default(span) => {
|
||||
panic!("Did not expect Default ret ty: {:?}", span);
|
||||
}
|
||||
};
|
||||
let mut d_ret_ty = match d_ret_ty.kind.clone() {
|
||||
TyKind::Tup(ref tys) => tys.clone(),
|
||||
TyKind::Path(_, rustc_ast::Path { segments, .. }) => {
|
||||
if let [segment] = &segments[..]
|
||||
&& segment.args.is_none()
|
||||
{
|
||||
let id = vec![segments[0].ident];
|
||||
let kind = TyKind::Path(None, ecx.path(span, id));
|
||||
let ty = P(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span, tokens: None });
|
||||
thin_vec![ty]
|
||||
} else {
|
||||
panic!("Expected tuple or simple path return type");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// We messed up construction of d_sig
|
||||
panic!("Did not expect non-tuple ret ty: {:?}", d_ret_ty);
|
||||
}
|
||||
};
|
||||
|
||||
if x.mode.is_fwd() && x.ret_activity == DiffActivity::Dual {
|
||||
assert!(d_ret_ty.len() == 2);
|
||||
// both should be identical, by construction
|
||||
let arg = d_ret_ty[0].kind.is_simple_path().unwrap();
|
||||
let arg2 = d_ret_ty[1].kind.is_simple_path().unwrap();
|
||||
assert!(arg == arg2);
|
||||
let sl: Vec<Symbol> = vec![arg, kw::Default];
|
||||
let tmp = ecx.def_site_path(&sl);
|
||||
let default_call_expr = ecx.expr_path(ecx.path(span, tmp));
|
||||
let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
|
||||
exprs.push(default_call_expr);
|
||||
} else if x.mode.is_rev() {
|
||||
if primal_ret {
|
||||
// We have extra handling above for the primal ret
|
||||
d_ret_ty = d_ret_ty[1..].to_vec().into();
|
||||
}
|
||||
|
||||
for arg in d_ret_ty.iter() {
|
||||
let arg = arg.kind.is_simple_path().unwrap();
|
||||
let sl: Vec<Symbol> = vec![arg, kw::Default];
|
||||
let tmp = ecx.def_site_path(&sl);
|
||||
let default_call_expr = ecx.expr_path(ecx.path(span, tmp));
|
||||
let default_call_expr =
|
||||
ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
|
||||
exprs.push(default_call_expr);
|
||||
}
|
||||
}
|
||||
|
||||
let ret: P<ast::Expr>;
|
||||
match &exprs[..] {
|
||||
[] => {
|
||||
assert!(!has_ret(&d_sig.decl.output));
|
||||
// We don't have to match the return type.
|
||||
return body;
|
||||
}
|
||||
[arg] => {
|
||||
ret = ecx
|
||||
.expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![arg.clone()]);
|
||||
}
|
||||
args => {
|
||||
let ret_tuple: P<ast::Expr> = ecx.expr_tuple(span, args.into());
|
||||
ret =
|
||||
ecx.expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![ret_tuple]);
|
||||
}
|
||||
}
|
||||
assert!(has_ret(&d_sig.decl.output));
|
||||
body.stmts.push(ecx.stmt_expr(ret));
|
||||
|
||||
body
|
||||
}
|
||||
|
||||
fn gen_primal_call(
|
||||
ecx: &ExtCtxt<'_>,
|
||||
span: Span,
|
||||
primal: Ident,
|
||||
idents: Vec<Ident>,
|
||||
) -> P<ast::Expr> {
|
||||
let has_self = idents.len() > 0 && idents[0].name == kw::SelfLower;
|
||||
if has_self {
|
||||
let args: ThinVec<_> =
|
||||
idents[1..].iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect();
|
||||
let self_expr = ecx.expr_self(span);
|
||||
ecx.expr_method_call(span, self_expr, primal, args.clone())
|
||||
} else {
|
||||
let args: ThinVec<_> =
|
||||
idents.iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect();
|
||||
let primal_call_expr = ecx.expr_path(ecx.path_ident(span, primal));
|
||||
ecx.expr_call(span, primal_call_expr, args)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the new function declaration. Const arguments are kept as is. Duplicated arguments must
|
||||
// be pointers or references. Those receive a shadow argument, which is a mutable reference/pointer.
|
||||
// Active arguments must be scalars. Their shadow argument is added to the return type (and will be
|
||||
// zero-initialized by Enzyme).
|
||||
// Each argument of the primal function (and the return type if existing) must be annotated with an
|
||||
// activity.
|
||||
//
|
||||
// Error handling: If the user provides an invalid configuration (incorrect numbers, types, or
|
||||
// both), we emit an error and return the original signature. This allows us to continue parsing.
|
||||
fn gen_enzyme_decl(
|
||||
ecx: &ExtCtxt<'_>,
|
||||
sig: &ast::FnSig,
|
||||
x: &AutoDiffAttrs,
|
||||
span: Span,
|
||||
) -> (ast::FnSig, Vec<String>, Vec<Ident>, bool) {
|
||||
let dcx = ecx.sess.dcx();
|
||||
let has_ret = has_ret(&sig.decl.output);
|
||||
let sig_args = sig.decl.inputs.len() + if has_ret { 1 } else { 0 };
|
||||
let num_activities = x.input_activity.len() + if x.has_ret_activity() { 1 } else { 0 };
|
||||
if sig_args != num_activities {
|
||||
dcx.emit_err(errors::AutoDiffInvalidNumberActivities {
|
||||
span,
|
||||
expected: sig_args,
|
||||
found: num_activities,
|
||||
});
|
||||
// This is not the right signature, but we can continue parsing.
|
||||
return (sig.clone(), vec![], vec![], true);
|
||||
}
|
||||
assert!(sig.decl.inputs.len() == x.input_activity.len());
|
||||
assert!(has_ret == x.has_ret_activity());
|
||||
let mut d_decl = sig.decl.clone();
|
||||
let mut d_inputs = Vec::new();
|
||||
let mut new_inputs = Vec::new();
|
||||
let mut idents = Vec::new();
|
||||
let mut act_ret = ThinVec::new();
|
||||
|
||||
// We have two loops, a first one just to check the activities and types and possibly report
|
||||
// multiple errors in one compilation session.
|
||||
let mut errors = false;
|
||||
for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) {
|
||||
if !valid_input_activity(x.mode, *activity) {
|
||||
dcx.emit_err(errors::AutoDiffInvalidApplicationModeAct {
|
||||
span,
|
||||
mode: x.mode.to_string(),
|
||||
act: activity.to_string(),
|
||||
});
|
||||
errors = true;
|
||||
}
|
||||
if !valid_ty_for_activity(&arg.ty, *activity) {
|
||||
dcx.emit_err(errors::AutoDiffInvalidTypeForActivity {
|
||||
span: arg.ty.span,
|
||||
act: activity.to_string(),
|
||||
});
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
if errors {
|
||||
// This is not the right signature, but we can continue parsing.
|
||||
return (sig.clone(), new_inputs, idents, true);
|
||||
}
|
||||
let unsafe_activities = x
|
||||
.input_activity
|
||||
.iter()
|
||||
.any(|&act| matches!(act, DiffActivity::DuplicatedOnly | DiffActivity::DualOnly));
|
||||
for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) {
|
||||
d_inputs.push(arg.clone());
|
||||
match activity {
|
||||
DiffActivity::Active => {
|
||||
act_ret.push(arg.ty.clone());
|
||||
}
|
||||
DiffActivity::ActiveOnly => {
|
||||
// We will add the active scalar to the return type.
|
||||
// This is handled later.
|
||||
}
|
||||
DiffActivity::Duplicated | DiffActivity::DuplicatedOnly => {
|
||||
let mut shadow_arg = arg.clone();
|
||||
// We += into the shadow in reverse mode.
|
||||
shadow_arg.ty = P(assure_mut_ref(&arg.ty));
|
||||
let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
|
||||
ident.name
|
||||
} else {
|
||||
debug!("{:#?}", &shadow_arg.pat);
|
||||
panic!("not an ident?");
|
||||
};
|
||||
let name: String = format!("d{}", old_name);
|
||||
new_inputs.push(name.clone());
|
||||
let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
|
||||
shadow_arg.pat = P(ast::Pat {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
kind: PatKind::Ident(BindingMode::NONE, ident, None),
|
||||
span: shadow_arg.pat.span,
|
||||
tokens: shadow_arg.pat.tokens.clone(),
|
||||
});
|
||||
d_inputs.push(shadow_arg);
|
||||
}
|
||||
DiffActivity::Dual | DiffActivity::DualOnly => {
|
||||
let mut shadow_arg = arg.clone();
|
||||
let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
|
||||
ident.name
|
||||
} else {
|
||||
debug!("{:#?}", &shadow_arg.pat);
|
||||
panic!("not an ident?");
|
||||
};
|
||||
let name: String = format!("b{}", old_name);
|
||||
new_inputs.push(name.clone());
|
||||
let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
|
||||
shadow_arg.pat = P(ast::Pat {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
kind: PatKind::Ident(BindingMode::NONE, ident, None),
|
||||
span: shadow_arg.pat.span,
|
||||
tokens: shadow_arg.pat.tokens.clone(),
|
||||
});
|
||||
d_inputs.push(shadow_arg);
|
||||
}
|
||||
DiffActivity::Const => {
|
||||
// Nothing to do here.
|
||||
}
|
||||
DiffActivity::None | DiffActivity::FakeActivitySize => {
|
||||
panic!("Should not happen");
|
||||
}
|
||||
}
|
||||
if let PatKind::Ident(_, ident, _) = arg.pat.kind {
|
||||
idents.push(ident.clone());
|
||||
} else {
|
||||
panic!("not an ident?");
|
||||
}
|
||||
}
|
||||
|
||||
let active_only_ret = x.ret_activity == DiffActivity::ActiveOnly;
|
||||
if active_only_ret {
|
||||
assert!(x.mode.is_rev());
|
||||
}
|
||||
|
||||
// If we return a scalar in the primal and the scalar is active,
|
||||
// then add it as last arg to the inputs.
|
||||
if x.mode.is_rev() {
|
||||
match x.ret_activity {
|
||||
DiffActivity::Active | DiffActivity::ActiveOnly => {
|
||||
let ty = match d_decl.output {
|
||||
FnRetTy::Ty(ref ty) => ty.clone(),
|
||||
FnRetTy::Default(span) => {
|
||||
panic!("Did not expect Default ret ty: {:?}", span);
|
||||
}
|
||||
};
|
||||
let name = "dret".to_string();
|
||||
let ident = Ident::from_str_and_span(&name, ty.span);
|
||||
let shadow_arg = ast::Param {
|
||||
attrs: ThinVec::new(),
|
||||
ty: ty.clone(),
|
||||
pat: P(ast::Pat {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
kind: PatKind::Ident(BindingMode::NONE, ident, None),
|
||||
span: ty.span,
|
||||
tokens: None,
|
||||
}),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: ty.span,
|
||||
is_placeholder: false,
|
||||
};
|
||||
d_inputs.push(shadow_arg);
|
||||
new_inputs.push(name);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
d_decl.inputs = d_inputs.into();
|
||||
|
||||
if x.mode.is_fwd() {
|
||||
if let DiffActivity::Dual = x.ret_activity {
|
||||
let ty = match d_decl.output {
|
||||
FnRetTy::Ty(ref ty) => ty.clone(),
|
||||
FnRetTy::Default(span) => {
|
||||
panic!("Did not expect Default ret ty: {:?}", span);
|
||||
}
|
||||
};
|
||||
// Dual can only be used for f32/f64 ret.
|
||||
// In that case we return now a tuple with two floats.
|
||||
let kind = TyKind::Tup(thin_vec![ty.clone(), ty.clone()]);
|
||||
let ty = P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None });
|
||||
d_decl.output = FnRetTy::Ty(ty);
|
||||
}
|
||||
if let DiffActivity::DualOnly = x.ret_activity {
|
||||
// No need to change the return type,
|
||||
// we will just return the shadow in place
|
||||
// of the primal return.
|
||||
}
|
||||
}
|
||||
|
||||
// If we use ActiveOnly, drop the original return value.
|
||||
d_decl.output =
|
||||
if active_only_ret { FnRetTy::Default(span) } else { d_decl.output.clone() };
|
||||
|
||||
trace!("act_ret: {:?}", act_ret);
|
||||
|
||||
// If we have an active input scalar, add it's gradient to the
|
||||
// return type. This might require changing the return type to a
|
||||
// tuple.
|
||||
if act_ret.len() > 0 {
|
||||
let ret_ty = match d_decl.output {
|
||||
FnRetTy::Ty(ref ty) => {
|
||||
if !active_only_ret {
|
||||
act_ret.insert(0, ty.clone());
|
||||
}
|
||||
let kind = TyKind::Tup(act_ret);
|
||||
P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None })
|
||||
}
|
||||
FnRetTy::Default(span) => {
|
||||
if act_ret.len() == 1 {
|
||||
act_ret[0].clone()
|
||||
} else {
|
||||
let kind = TyKind::Tup(act_ret.iter().map(|arg| arg.clone()).collect());
|
||||
P(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span, tokens: None })
|
||||
}
|
||||
}
|
||||
};
|
||||
d_decl.output = FnRetTy::Ty(ret_ty);
|
||||
}
|
||||
|
||||
let mut d_header = sig.header.clone();
|
||||
if unsafe_activities {
|
||||
d_header.safety = rustc_ast::Safety::Unsafe(span);
|
||||
}
|
||||
let d_sig = FnSig { header: d_header, decl: d_decl, span };
|
||||
trace!("Generated signature: {:?}", d_sig);
|
||||
(d_sig, new_inputs, idents, false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
mod ad_fallback {
|
||||
use rustc_ast::ast;
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::errors;
|
||||
pub(crate) fn expand(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
_expand_span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span });
|
||||
return vec![item];
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
pub(crate) use ad_fallback::expand;
|
||||
#[cfg(llvm_enzyme)]
|
||||
pub(crate) use llvm_enzyme::expand;
|
|
@ -113,7 +113,7 @@ fn cs_clone_simple(
|
|||
// Already produced an assertion for this type.
|
||||
// Anonymous structs or unions must be eliminated as they cannot be
|
||||
// type parameters.
|
||||
} else if !field.ty.kind.is_anon_adt() {
|
||||
} else {
|
||||
// let _: AssertParamIsClone<FieldTy>;
|
||||
super::assert_ty_bounds(cx, &mut stmts, field.ty.clone(), field.span, &[
|
||||
sym::clone,
|
||||
|
|
|
@ -124,8 +124,6 @@ fn assert_ty_bounds(
|
|||
span: Span,
|
||||
assert_path: &[Symbol],
|
||||
) {
|
||||
// Deny anonymous structs or unions to avoid weird errors.
|
||||
assert!(!ty.kind.is_anon_adt(), "Anonymous structs or unions cannot be type parameters");
|
||||
// Generate statement `let _: assert_path<ty>;`.
|
||||
let span = cx.with_def_site_ctxt(span);
|
||||
let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]);
|
||||
|
|
|
@ -312,7 +312,7 @@ pub(crate) fn expand_deriving_smart_ptr(
|
|||
impl_generics.params.insert(pointee_param_idx + 1, extra_param);
|
||||
|
||||
// Add the impl blocks for `DispatchFromDyn` and `CoerceUnsized`.
|
||||
let gen_args = vec![GenericArg::Type(alt_self_type.clone())];
|
||||
let gen_args = vec![GenericArg::Type(alt_self_type)];
|
||||
add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
|
||||
add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args);
|
||||
}
|
||||
|
@ -333,12 +333,12 @@ fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee:
|
|||
}
|
||||
|
||||
fn is_maybe_sized_bound(bound: &GenericBound) -> bool {
|
||||
if let GenericBound::Trait(
|
||||
trait_ref,
|
||||
TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. },
|
||||
) = bound
|
||||
if let GenericBound::Trait(trait_ref) = bound
|
||||
&& let TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. } =
|
||||
trait_ref.modifiers
|
||||
&& is_sized_marker(&trait_ref.trait_ref.path)
|
||||
{
|
||||
is_sized_marker(&trait_ref.trait_ref.path)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use rustc_span::symbol::{Ident, Symbol, kw, sym};
|
|||
use thin_vec::thin_vec;
|
||||
|
||||
use crate::errors;
|
||||
use crate::util::{expr_to_string, get_exprs_from_tts, get_single_str_from_tts};
|
||||
use crate::util::{expr_to_string, get_exprs_from_tts, get_single_expr_from_tts};
|
||||
|
||||
fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result<Symbol, VarError> {
|
||||
let var = var.as_str();
|
||||
|
@ -32,19 +32,28 @@ pub(crate) fn expand_option_env<'cx>(
|
|||
sp: Span,
|
||||
tts: TokenStream,
|
||||
) -> MacroExpanderResult<'cx> {
|
||||
let ExpandResult::Ready(mac) = get_single_str_from_tts(cx, sp, tts, "option_env!") else {
|
||||
let ExpandResult::Ready(mac_expr) = get_single_expr_from_tts(cx, sp, tts, "option_env!") else {
|
||||
return ExpandResult::Retry(());
|
||||
};
|
||||
let var_expr = match mac_expr {
|
||||
Ok(var_expr) => var_expr,
|
||||
Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
|
||||
};
|
||||
let ExpandResult::Ready(mac) =
|
||||
expr_to_string(cx, var_expr.clone(), "argument must be a string literal")
|
||||
else {
|
||||
return ExpandResult::Retry(());
|
||||
};
|
||||
let var = match mac {
|
||||
Ok(var) => var,
|
||||
Ok((var, _)) => var,
|
||||
Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
|
||||
};
|
||||
|
||||
let sp = cx.with_def_site_ctxt(sp);
|
||||
let value = lookup_env(cx, var).ok();
|
||||
cx.sess.psess.env_depinfo.borrow_mut().insert((var, value));
|
||||
let value = lookup_env(cx, var);
|
||||
cx.sess.psess.env_depinfo.borrow_mut().insert((var, value.as_ref().ok().copied()));
|
||||
let e = match value {
|
||||
None => {
|
||||
Err(VarError::NotPresent) => {
|
||||
let lt = cx.lifetime(sp, Ident::new(kw::StaticLifetime, sp));
|
||||
cx.expr_path(cx.path_all(
|
||||
sp,
|
||||
|
@ -58,7 +67,18 @@ pub(crate) fn expand_option_env<'cx>(
|
|||
))],
|
||||
))
|
||||
}
|
||||
Some(value) => {
|
||||
Err(VarError::NotUnicode(_)) => {
|
||||
let ExprKind::Lit(token::Lit {
|
||||
kind: LitKind::Str | LitKind::StrRaw(..), symbol, ..
|
||||
}) = &var_expr.kind
|
||||
else {
|
||||
unreachable!("`expr_to_string` ensures this is a string lit")
|
||||
};
|
||||
|
||||
let guar = cx.dcx().emit_err(errors::EnvNotUnicode { span: sp, var: *symbol });
|
||||
return ExpandResult::Ready(DummyResult::any(sp, guar));
|
||||
}
|
||||
Ok(value) => {
|
||||
cx.expr_call_global(sp, cx.std_path(&[sym::option, sym::Option, sym::Some]), thin_vec![
|
||||
cx.expr_str(sp, value)
|
||||
])
|
||||
|
|
|
@ -145,6 +145,78 @@ pub(crate) struct AllocMustStatics {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[cfg(llvm_enzyme)]
|
||||
pub(crate) use autodiff::*;
|
||||
|
||||
#[cfg(llvm_enzyme)]
|
||||
mod autodiff {
|
||||
use super::*;
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_missing_config)]
|
||||
pub(crate) struct AutoDiffMissingConfig {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_unknown_activity)]
|
||||
pub(crate) struct AutoDiffUnknownActivity {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) act: String,
|
||||
}
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_ty_activity)]
|
||||
pub(crate) struct AutoDiffInvalidTypeForActivity {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) act: String,
|
||||
}
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_number_activities)]
|
||||
pub(crate) struct AutoDiffInvalidNumberActivities {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) expected: usize,
|
||||
pub(crate) found: usize,
|
||||
}
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_mode_activity)]
|
||||
pub(crate) struct AutoDiffInvalidApplicationModeAct {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) mode: String,
|
||||
pub(crate) act: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_mode)]
|
||||
pub(crate) struct AutoDiffInvalidMode {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) mode: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff)]
|
||||
pub(crate) struct AutoDiffInvalidApplication {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
pub(crate) use ad_fallback::*;
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
mod ad_fallback {
|
||||
use super::*;
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_not_build)]
|
||||
pub(crate) struct AutoDiffSupportNotBuild {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_concat_bytes_invalid)]
|
||||
pub(crate) struct ConcatBytesInvalid {
|
||||
|
|
|
@ -195,12 +195,26 @@ fn make_format_args(
|
|||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
let sugg_fmt = match args.explicit_args().len() {
|
||||
0 => "{}".to_string(),
|
||||
count => {
|
||||
format!("{}{{}}", "{} ".repeat(count))
|
||||
// `{}` or `()`
|
||||
let should_suggest = |kind: &ExprKind| -> bool {
|
||||
match kind {
|
||||
ExprKind::Block(b, None) if b.stmts.is_empty() => true,
|
||||
ExprKind::Tup(v) if v.is_empty() => true,
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
|
||||
let mut sugg_fmt = String::new();
|
||||
for kind in std::iter::once(&efmt.kind)
|
||||
.chain(args.explicit_args().into_iter().map(|a| &a.expr.kind))
|
||||
{
|
||||
sugg_fmt.push_str(if should_suggest(kind) {
|
||||
"{:?} "
|
||||
} else {
|
||||
"{} "
|
||||
});
|
||||
}
|
||||
sugg_fmt = sugg_fmt.trim_end().to_string();
|
||||
err.span_suggestion(
|
||||
unexpanded_fmt_span.shrink_to_lo(),
|
||||
"you might be missing a string literal to format with",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(not(bootstrap), feature(autodiff))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
|
@ -29,6 +30,7 @@ use crate::deriving::*;
|
|||
|
||||
mod alloc_error_handler;
|
||||
mod assert;
|
||||
mod autodiff;
|
||||
mod cfg;
|
||||
mod cfg_accessible;
|
||||
mod cfg_eval;
|
||||
|
@ -106,6 +108,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||
|
||||
register_attr! {
|
||||
alloc_error_handler: alloc_error_handler::expand,
|
||||
autodiff: autodiff::expand,
|
||||
bench: test::expand_bench,
|
||||
cfg_accessible: cfg_accessible::Expander,
|
||||
cfg_eval: cfg_eval::expand,
|
||||
|
|
|
@ -171,6 +171,30 @@ pub(crate) fn get_single_str_spanned_from_tts(
|
|||
tts: TokenStream,
|
||||
name: &str,
|
||||
) -> ExpandResult<Result<(Symbol, Span), ErrorGuaranteed>, ()> {
|
||||
let ExpandResult::Ready(ret) = get_single_expr_from_tts(cx, span, tts, name) else {
|
||||
return ExpandResult::Retry(());
|
||||
};
|
||||
let ret = match ret {
|
||||
Ok(ret) => ret,
|
||||
Err(e) => return ExpandResult::Ready(Err(e)),
|
||||
};
|
||||
expr_to_spanned_string(cx, ret, "argument must be a string literal").map(|res| {
|
||||
res.map_err(|err| match err {
|
||||
Ok((err, _)) => err.emit(),
|
||||
Err(guar) => guar,
|
||||
})
|
||||
.map(|(symbol, _style, span)| (symbol, span))
|
||||
})
|
||||
}
|
||||
|
||||
/// Interpreting `tts` as a comma-separated sequence of expressions,
|
||||
/// expect exactly one expression, or emit an error and return `Err`.
|
||||
pub(crate) fn get_single_expr_from_tts(
|
||||
cx: &mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
tts: TokenStream,
|
||||
name: &str,
|
||||
) -> ExpandResult<Result<P<ast::Expr>, ErrorGuaranteed>, ()> {
|
||||
let mut p = cx.new_parser_from_tts(tts);
|
||||
if p.token == token::Eof {
|
||||
let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
|
||||
|
@ -185,13 +209,7 @@ pub(crate) fn get_single_str_spanned_from_tts(
|
|||
if p.token != token::Eof {
|
||||
cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
|
||||
}
|
||||
expr_to_spanned_string(cx, ret, "argument must be a string literal").map(|res| {
|
||||
res.map_err(|err| match err {
|
||||
Ok((err, _)) => err.emit(),
|
||||
Err(guar) => guar,
|
||||
})
|
||||
.map(|(symbol, _style, span)| (symbol, span))
|
||||
})
|
||||
ExpandResult::Ready(Ok(ret))
|
||||
}
|
||||
|
||||
/// Extracts comma-separated expressions from `tts`.
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
//! Adapted from <https://github.com/rust-lang/rust/blob/31c0645b9d2539f47eecb096142474b29dc542f7/compiler/rustc_codegen_ssa/src/mir/place.rs>
|
||||
//! (<https://github.com/rust-lang/rust/pull/104535>)
|
||||
|
||||
use rustc_target::abi::{Int, TagEncoding, Variants};
|
||||
use rustc_abi::Primitive::Int;
|
||||
use rustc_abi::{TagEncoding, Variants};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
|
|
|
@ -328,6 +328,9 @@ fn codegen_float_intrinsic_call<'tcx>(
|
|||
sym::fabsf64 => ("fabs", 1, fx.tcx.types.f64, types::F64),
|
||||
sym::fmaf32 => ("fmaf", 3, fx.tcx.types.f32, types::F32),
|
||||
sym::fmaf64 => ("fma", 3, fx.tcx.types.f64, types::F64),
|
||||
// FIXME: calling `fma` from libc without FMA target feature uses expensive sofware emulation
|
||||
sym::fmuladdf32 => ("fmaf", 3, fx.tcx.types.f32, types::F32), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f32
|
||||
sym::fmuladdf64 => ("fma", 3, fx.tcx.types.f64, types::F64), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f64
|
||||
sym::copysignf32 => ("copysignf", 2, fx.tcx.types.f32, types::F32),
|
||||
sym::copysignf64 => ("copysign", 2, fx.tcx.types.f64, types::F64),
|
||||
sym::floorf32 => ("floorf", 1, fx.tcx.types.f32, types::F32),
|
||||
|
@ -381,7 +384,7 @@ fn codegen_float_intrinsic_call<'tcx>(
|
|||
|
||||
let layout = fx.layout_of(ty);
|
||||
let res = match intrinsic {
|
||||
sym::fmaf32 | sym::fmaf64 => {
|
||||
sym::fmaf32 | sym::fmaf64 | sym::fmuladdf32 | sym::fmuladdf64 => {
|
||||
CValue::by_val(fx.bcx.ins().fma(args[0], args[1], args[2]), layout)
|
||||
}
|
||||
sym::copysignf32 | sym::copysignf64 => {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
extern crate jobserver;
|
||||
#[macro_use]
|
||||
extern crate rustc_middle;
|
||||
extern crate rustc_abi;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_codegen_ssa;
|
||||
extern crate rustc_data_structures;
|
||||
|
|
|
@ -8,9 +8,6 @@ codegen_gcc_invalid_minimum_alignment =
|
|||
codegen_gcc_lto_not_supported =
|
||||
LTO is not supported. You may get a linker error.
|
||||
|
||||
codegen_gcc_tied_target_features = the target features {$features} must all be either enabled or disabled together
|
||||
.help = add the missing features in a `target_feature` attribute
|
||||
|
||||
codegen_gcc_unwinding_inline_asm =
|
||||
GCC backend does not support unwinding from inline asm
|
||||
|
||||
|
|
|
@ -7,11 +7,9 @@ use rustc_attr::InstructionSetAttr;
|
|||
#[cfg(feature = "master")]
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use crate::context::CodegenCx;
|
||||
use crate::errors::TiedTargetFeatures;
|
||||
use crate::gcc_util::{check_tied_features, to_gcc_features};
|
||||
use crate::gcc_util::to_gcc_features;
|
||||
|
||||
/// Get GCC attribute for the provided inline heuristic.
|
||||
#[cfg(feature = "master")]
|
||||
|
@ -72,26 +70,10 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
let function_features = codegen_fn_attrs
|
||||
let mut function_features = codegen_fn_attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.map(|features| features.name.as_str())
|
||||
.collect::<Vec<&str>>();
|
||||
|
||||
if let Some(features) = check_tied_features(
|
||||
cx.tcx.sess,
|
||||
&function_features.iter().map(|features| (*features, true)).collect(),
|
||||
) {
|
||||
let span = cx
|
||||
.tcx
|
||||
.get_attr(instance.def_id(), sym::target_feature)
|
||||
.map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span);
|
||||
cx.tcx.dcx().create_err(TiedTargetFeatures { features: features.join(", "), span }).emit();
|
||||
return;
|
||||
}
|
||||
|
||||
let mut function_features = function_features
|
||||
.iter()
|
||||
.flat_map(|feat| to_gcc_features(cx.tcx.sess, feat).into_iter())
|
||||
.chain(codegen_fn_attrs.instruction_set.iter().map(|x| match *x {
|
||||
InstructionSetAttr::ArmA32 => "-thumb-mode", // TODO(antoyo): support removing feature.
|
||||
|
|
|
@ -7,6 +7,8 @@ use gccjit::{
|
|||
BinaryOp, Block, ComparisonOp, Context, Function, LValue, Location, RValue, ToRValue, Type,
|
||||
UnaryOp,
|
||||
};
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout, WrappingRange};
|
||||
use rustc_apfloat::{Float, Round, Status, ieee};
|
||||
use rustc_codegen_ssa::MemFlags;
|
||||
use rustc_codegen_ssa::common::{
|
||||
|
@ -28,7 +30,6 @@ use rustc_middle::ty::{Instance, ParamEnv, Ty, TyCtxt};
|
|||
use rustc_span::Span;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout, WrappingRange};
|
||||
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi};
|
||||
|
||||
use crate::common::{SignType, TypeReflection, type_is_pointer};
|
||||
|
@ -998,12 +999,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
) {
|
||||
let vr = scalar.valid_range(bx);
|
||||
match scalar.primitive() {
|
||||
abi::Int(..) => {
|
||||
abi::Primitive::Int(..) => {
|
||||
if !scalar.is_always_valid(bx) {
|
||||
bx.range_metadata(load, vr);
|
||||
}
|
||||
}
|
||||
abi::Pointer(_) if vr.start < vr.end && !vr.contains(0) => {
|
||||
abi::Primitive::Pointer(_) if vr.start < vr.end && !vr.contains(0) => {
|
||||
bx.nonnull_metadata(load);
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use gccjit::{LValue, RValue, ToRValue, Type};
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::HasDataLayout;
|
||||
use rustc_abi::Primitive::Pointer;
|
||||
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::ty::layout::LayoutOf;
|
||||
use rustc_target::abi::{self, HasDataLayout, Pointer};
|
||||
|
||||
use crate::consts::const_alloc_to_gcc;
|
||||
use crate::context::CodegenCx;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_unknown_ctarget_feature_prefix)]
|
||||
#[note]
|
||||
|
@ -45,15 +42,6 @@ pub(crate) struct InvalidMinimumAlignment {
|
|||
pub err: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_tied_target_features)]
|
||||
#[help]
|
||||
pub(crate) struct TiedTargetFeatures {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub features: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_copy_bitcode)]
|
||||
pub(crate) struct CopyBitcode {
|
||||
|
@ -78,27 +66,3 @@ pub(crate) struct LtoDylib;
|
|||
pub(crate) struct LtoBitcodeFromRlib {
|
||||
pub gcc_err: String,
|
||||
}
|
||||
|
||||
pub(crate) struct TargetFeatureDisableOrEnable<'a> {
|
||||
pub features: &'a [&'a str],
|
||||
pub span: Option<Span>,
|
||||
pub missing_features: Option<MissingFeatures>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[help(codegen_gcc_missing_features)]
|
||||
pub(crate) struct MissingFeatures;
|
||||
|
||||
impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
|
||||
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
|
||||
let mut diag = Diag::new(dcx, level, fluent::codegen_gcc_target_feature_disable_or_enable);
|
||||
if let Some(span) = self.span {
|
||||
diag.span(span);
|
||||
};
|
||||
if let Some(missing_features) = self.missing_features {
|
||||
diag.subdiagnostic(missing_features);
|
||||
}
|
||||
diag.arg("features", self.features.join(", "));
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
#[cfg(feature = "master")]
|
||||
use gccjit::Context;
|
||||
use rustc_codegen_ssa::codegen_attrs::check_tied_features;
|
||||
use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::bug;
|
||||
use rustc_session::Session;
|
||||
use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
|
||||
use crate::errors::{
|
||||
PossibleFeature, TargetFeatureDisableOrEnable, UnknownCTargetFeature,
|
||||
UnknownCTargetFeaturePrefix,
|
||||
};
|
||||
use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
|
||||
|
||||
/// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
|
||||
/// `--target` and similar).
|
||||
|
@ -185,23 +184,6 @@ pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]>
|
|||
}
|
||||
}
|
||||
|
||||
// Given a map from target_features to whether they are enabled or disabled,
|
||||
// ensure only valid combinations are allowed.
|
||||
pub fn check_tied_features(
|
||||
sess: &Session,
|
||||
features: &FxHashMap<&str, bool>,
|
||||
) -> Option<&'static [&'static str]> {
|
||||
for tied in sess.target.tied_target_features() {
|
||||
// Tied features must be set to the same value, or not set at all
|
||||
let mut tied_iter = tied.iter();
|
||||
let enabled = features.get(tied_iter.next().unwrap());
|
||||
if tied_iter.any(|feature| enabled != features.get(feature)) {
|
||||
return Some(tied);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn arch_to_gcc(name: &str) -> &str {
|
||||
match name {
|
||||
"M68020" => "68020",
|
||||
|
|
|
@ -66,6 +66,9 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
|
|||
sym::log2f64 => "log2",
|
||||
sym::fmaf32 => "fmaf",
|
||||
sym::fmaf64 => "fma",
|
||||
// FIXME: calling `fma` from libc without FMA target feature uses expensive sofware emulation
|
||||
sym::fmuladdf32 => "fmaf", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f32
|
||||
sym::fmuladdf64 => "fma", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f64
|
||||
sym::fabsf32 => "fabsf",
|
||||
sym::fabsf64 => "fabs",
|
||||
sym::minnumf32 => "fminf",
|
||||
|
|
|
@ -32,6 +32,7 @@ extern crate tempfile;
|
|||
extern crate tracing;
|
||||
|
||||
// The rustc crates we need
|
||||
extern crate rustc_abi;
|
||||
extern crate rustc_apfloat;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_attr;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use gccjit::{Struct, Type};
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::Primitive::*;
|
||||
use rustc_abi::{Abi, FieldsShape, Integer, PointeeInfo, Size, Variants};
|
||||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, DerivedTypeCodegenMethods, LayoutTypeCodegenMethods,
|
||||
};
|
||||
|
@ -8,11 +11,8 @@ use rustc_middle::bug;
|
|||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt};
|
||||
use rustc_target::abi::TyAbiInterface;
|
||||
use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
|
||||
use rustc_target::abi::{
|
||||
self, Abi, FieldsShape, Float, Int, Integer, PointeeInfo, Pointer, Size, TyAbiInterface,
|
||||
Variants,
|
||||
};
|
||||
|
||||
use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType};
|
||||
use crate::context::CodegenCx;
|
||||
|
|
|
@ -14,6 +14,7 @@ libc = "0.2"
|
|||
measureme = "11"
|
||||
object = { version = "0.36.3", default-features = false, features = ["std", "read"] }
|
||||
rustc-demangle = "0.1.21"
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
|
||||
|
|
|
@ -33,9 +33,6 @@ codegen_llvm_lto_proc_macro = lto cannot be used for `proc-macro` crate type wit
|
|||
codegen_llvm_mismatch_data_layout =
|
||||
data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}`
|
||||
|
||||
codegen_llvm_missing_features =
|
||||
add the missing features in a `target_feature` attribute
|
||||
|
||||
codegen_llvm_multiple_source_dicompileunit = multiple source DICompileUnits found
|
||||
codegen_llvm_multiple_source_dicompileunit_with_llvm_err = multiple source DICompileUnits found: {$llvm_err}
|
||||
|
||||
|
@ -63,9 +60,6 @@ codegen_llvm_serialize_module_with_llvm_err = failed to serialize module {$name}
|
|||
codegen_llvm_symbol_already_defined =
|
||||
symbol `{$symbol_name}` is already defined
|
||||
|
||||
codegen_llvm_target_feature_disable_or_enable =
|
||||
the target features {$features} must all be either enabled or disabled together
|
||||
|
||||
codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple}
|
||||
codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::cmp;
|
||||
|
||||
use libc::c_uint;
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::Primitive::Int;
|
||||
use rustc_abi::{HasDataLayout, Size};
|
||||
use rustc_codegen_ssa::MemFlags;
|
||||
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
|
||||
use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue};
|
||||
|
@ -11,7 +14,6 @@ pub(crate) use rustc_middle::ty::layout::{WIDE_PTR_ADDR, WIDE_PTR_EXTRA};
|
|||
use rustc_middle::{bug, ty};
|
||||
use rustc_session::config;
|
||||
pub(crate) use rustc_target::abi::call::*;
|
||||
use rustc_target::abi::{self, HasDataLayout, Int, Size};
|
||||
use rustc_target::spec::SanitizerSet;
|
||||
pub(crate) use rustc_target::spec::abi::Abi;
|
||||
use smallvec::SmallVec;
|
||||
|
|
|
@ -154,7 +154,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
// We prefer the latter because it matches the behavior of
|
||||
// Clang.
|
||||
if late && matches!(reg, InlineAsmRegOrRegClass::Reg(_)) {
|
||||
constraints.push(reg_to_llvm(reg, Some(&in_value.layout)).to_string());
|
||||
constraints.push(reg_to_llvm(reg, Some(&in_value.layout)));
|
||||
} else {
|
||||
constraints.push(format!("{}", op_idx[&idx]));
|
||||
}
|
||||
|
@ -542,57 +542,16 @@ fn xmm_reg_index(reg: InlineAsmReg) -> Option<u32> {
|
|||
|
||||
/// If the register is an AArch64 integer register then return its index.
|
||||
fn a64_reg_index(reg: InlineAsmReg) -> Option<u32> {
|
||||
use AArch64InlineAsmReg::*;
|
||||
// Unlike `a64_vreg_index`, we can't subtract `x0` to get the u32 because
|
||||
// `x19` and `x29` are missing and the integer constants for the
|
||||
// `x0`..`x30` enum variants don't all match the register number. E.g. the
|
||||
// integer constant for `x18` is 18, but the constant for `x20` is 19.
|
||||
Some(match reg {
|
||||
InlineAsmReg::AArch64(r) => match r {
|
||||
x0 => 0,
|
||||
x1 => 1,
|
||||
x2 => 2,
|
||||
x3 => 3,
|
||||
x4 => 4,
|
||||
x5 => 5,
|
||||
x6 => 6,
|
||||
x7 => 7,
|
||||
x8 => 8,
|
||||
x9 => 9,
|
||||
x10 => 10,
|
||||
x11 => 11,
|
||||
x12 => 12,
|
||||
x13 => 13,
|
||||
x14 => 14,
|
||||
x15 => 15,
|
||||
x16 => 16,
|
||||
x17 => 17,
|
||||
x18 => 18,
|
||||
// x19 is reserved
|
||||
x20 => 20,
|
||||
x21 => 21,
|
||||
x22 => 22,
|
||||
x23 => 23,
|
||||
x24 => 24,
|
||||
x25 => 25,
|
||||
x26 => 26,
|
||||
x27 => 27,
|
||||
x28 => 28,
|
||||
// x29 is reserved
|
||||
x30 => 30,
|
||||
_ => return None,
|
||||
},
|
||||
_ => return None,
|
||||
})
|
||||
match reg {
|
||||
InlineAsmReg::AArch64(r) => r.reg_index(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// If the register is an AArch64 vector register then return its index.
|
||||
fn a64_vreg_index(reg: InlineAsmReg) -> Option<u32> {
|
||||
use AArch64InlineAsmReg::*;
|
||||
match reg {
|
||||
InlineAsmReg::AArch64(reg) if reg as u32 >= v0 as u32 && reg as u32 <= v31 as u32 => {
|
||||
Some(reg as u32 - v0 as u32)
|
||||
}
|
||||
InlineAsmReg::AArch64(reg) => reg.vreg_index(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,11 @@ use rustc_hir::def_id::DefId;
|
|||
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::context::CodegenCx;
|
||||
use crate::errors::{MissingFeatures, SanitizerMemtagRequiresMte, TargetFeatureDisableOrEnable};
|
||||
use crate::errors::SanitizerMemtagRequiresMte;
|
||||
use crate::llvm::AttributePlace::Function;
|
||||
use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects};
|
||||
use crate::value::Value;
|
||||
|
@ -502,26 +501,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
|||
let function_features =
|
||||
codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>();
|
||||
|
||||
if let Some(f) = llvm_util::check_tied_features(
|
||||
cx.tcx.sess,
|
||||
&function_features.iter().map(|f| (*f, true)).collect(),
|
||||
) {
|
||||
let span = cx
|
||||
.tcx
|
||||
.get_attrs(instance.def_id(), sym::target_feature)
|
||||
.next()
|
||||
.map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span);
|
||||
cx.tcx
|
||||
.dcx()
|
||||
.create_err(TargetFeatureDisableOrEnable {
|
||||
features: f,
|
||||
span: Some(span),
|
||||
missing_features: Some(MissingFeatures),
|
||||
})
|
||||
.emit();
|
||||
return;
|
||||
}
|
||||
|
||||
let function_features = function_features
|
||||
.iter()
|
||||
// Convert to LLVMFeatures and filter out unavailable ones
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::ops::Deref;
|
|||
use std::{iter, ptr};
|
||||
|
||||
use libc::{c_char, c_uint};
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::{Align, Size, WrappingRange};
|
||||
use rustc_codegen_ssa::MemFlags;
|
||||
use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind};
|
||||
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
|
||||
|
@ -20,7 +22,6 @@ use rustc_sanitizers::{cfi, kcfi};
|
|||
use rustc_session::config::OptLevel;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use rustc_target::abi::{self, Align, Size, WrappingRange};
|
||||
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
|
||||
use smallvec::SmallVec;
|
||||
use tracing::{debug, instrument};
|
||||
|
@ -505,12 +506,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
}
|
||||
|
||||
match scalar.primitive() {
|
||||
abi::Int(..) => {
|
||||
abi::Primitive::Int(..) => {
|
||||
if !scalar.is_always_valid(bx) {
|
||||
bx.range_metadata(load, scalar.valid_range(bx));
|
||||
}
|
||||
}
|
||||
abi::Pointer(_) => {
|
||||
abi::Primitive::Pointer(_) => {
|
||||
if !scalar.valid_range(bx).contains(0) {
|
||||
bx.nonnull_metadata(load);
|
||||
}
|
||||
|
@ -521,7 +522,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
abi::Float(_) => {}
|
||||
abi::Primitive::Float(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
//! Code that is useful in various codegen modules.
|
||||
|
||||
use libc::{c_char, c_uint};
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::Primitive::Pointer;
|
||||
use rustc_abi::{AddressSpace, HasDataLayout};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_codegen_ssa::common::TypeKind;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
@ -9,7 +13,6 @@ use rustc_middle::bug;
|
|||
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::cstore::DllImport;
|
||||
use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::consts::const_alloc_to_llvm;
|
||||
|
@ -144,6 +147,10 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
}
|
||||
|
||||
fn const_int(&self, t: &'ll Type, i: i64) -> &'ll Value {
|
||||
debug_assert!(
|
||||
self.type_kind(t) == TypeKind::Integer,
|
||||
"only allows integer types in const_int"
|
||||
);
|
||||
unsafe { llvm::LLVMConstInt(t, i as u64, True) }
|
||||
}
|
||||
|
||||
|
@ -174,10 +181,18 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
}
|
||||
|
||||
fn const_uint(&self, t: &'ll Type, i: u64) -> &'ll Value {
|
||||
debug_assert!(
|
||||
self.type_kind(t) == TypeKind::Integer,
|
||||
"only allows integer types in const_uint"
|
||||
);
|
||||
unsafe { llvm::LLVMConstInt(t, i, False) }
|
||||
}
|
||||
|
||||
fn const_uint_big(&self, t: &'ll Type, u: u128) -> &'ll Value {
|
||||
debug_assert!(
|
||||
self.type_kind(t) == TypeKind::Integer,
|
||||
"only allows integer types in const_uint_big"
|
||||
);
|
||||
unsafe {
|
||||
let words = [u as u64, (u >> 64) as u64];
|
||||
llvm::LLVMConstIntOfArbitraryPrecision(t, 2, words.as_ptr())
|
||||
|
|
|
@ -138,6 +138,16 @@ pub(crate) unsafe fn create_module<'ll>(
|
|||
}
|
||||
}
|
||||
|
||||
if llvm_version < (20, 0, 0) {
|
||||
if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") {
|
||||
// LLVM 20 defines three additional address spaces for alternate
|
||||
// pointer kinds used in Windows.
|
||||
// See https://github.com/llvm/llvm-project/pull/111879
|
||||
target_data_layout =
|
||||
target_data_layout.replace("-p270:32:32-p271:32:32-p272:64:64", "");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the data-layout values hardcoded remain the defaults.
|
||||
{
|
||||
let tm = crate::back::write::create_informational_target_machine(tcx.sess, false);
|
||||
|
@ -884,6 +894,11 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64);
|
||||
ifn!("llvm.fma.f128", fn(t_f128, t_f128, t_f128) -> t_f128);
|
||||
|
||||
ifn!("llvm.fmuladd.f16", fn(t_f16, t_f16, t_f16) -> t_f16);
|
||||
ifn!("llvm.fmuladd.f32", fn(t_f32, t_f32, t_f32) -> t_f32);
|
||||
ifn!("llvm.fmuladd.f64", fn(t_f64, t_f64, t_f64) -> t_f64);
|
||||
ifn!("llvm.fmuladd.f128", fn(t_f128, t_f128, t_f128) -> t_f128);
|
||||
|
||||
ifn!("llvm.fabs.f16", fn(t_f16) -> t_f16);
|
||||
ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32);
|
||||
ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64);
|
||||
|
@ -1187,10 +1202,11 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
|
|||
span: Span,
|
||||
fn_abi_request: FnAbiRequest<'tcx>,
|
||||
) -> ! {
|
||||
if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
|
||||
self.tcx.dcx().emit_fatal(Spanned { span, node: err })
|
||||
} else {
|
||||
match fn_abi_request {
|
||||
match err {
|
||||
FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::Cycle(_)) => {
|
||||
self.tcx.dcx().emit_fatal(Spanned { span, node: err });
|
||||
}
|
||||
_ => match fn_abi_request {
|
||||
FnAbiRequest::OfFnPtr { sig, extra_args } => {
|
||||
span_bug!(span, "`fn_abi_of_fn_ptr({sig}, {extra_args:?})` failed: {err:?}",);
|
||||
}
|
||||
|
@ -1200,7 +1216,7 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
|
|||
"`fn_abi_of_instance({instance}, {extra_args:?})` failed: {err:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,29 +14,20 @@ use crate::coverageinfo::ffi::CounterMappingRegion;
|
|||
use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector};
|
||||
use crate::{coverageinfo, llvm};
|
||||
|
||||
/// Generates and exports the Coverage Map.
|
||||
/// Generates and exports the coverage map, which is embedded in special
|
||||
/// linker sections in the final binary.
|
||||
///
|
||||
/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions
|
||||
/// 6 and 7 (encoded as 5 and 6 respectively), as described at
|
||||
/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/18.0-2024-02-13/llvm/docs/CoverageMappingFormat.rst).
|
||||
/// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`)
|
||||
/// distributed in the `llvm-tools-preview` rustup component.
|
||||
///
|
||||
/// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
|
||||
/// the same version. Clang's implementation of Coverage Map generation was referenced when
|
||||
/// implementing this Rust version, and though the format documentation is very explicit and
|
||||
/// detailed, some undocumented details in Clang's implementation (that may or may not be important)
|
||||
/// were also replicated for Rust's Coverage Map.
|
||||
/// Those sections are then read and understood by LLVM's `llvm-cov` tool,
|
||||
/// which is distributed in the `llvm-tools` rustup component.
|
||||
pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
||||
let tcx = cx.tcx;
|
||||
|
||||
// Ensure that LLVM is using a version of the coverage mapping format that
|
||||
// agrees with our Rust-side code. Expected versions (encoded as n-1) are:
|
||||
// - `CovMapVersion::Version6` (5) used by LLVM 13-17
|
||||
// - `CovMapVersion::Version7` (6) used by LLVM 18
|
||||
// - `CovMapVersion::Version7` (6) used by LLVM 18-19
|
||||
let covmap_version = {
|
||||
let llvm_covmap_version = coverageinfo::mapping_version();
|
||||
let expected_versions = 5..=6;
|
||||
let expected_versions = 6..=6;
|
||||
assert!(
|
||||
expected_versions.contains(&llvm_covmap_version),
|
||||
"Coverage mapping version exposed by `llvm-wrapper` is out of sync; \
|
||||
|
|
|
@ -84,10 +84,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
|||
unnamed: llvm::UnnamedAddr,
|
||||
fn_type: &'ll Type,
|
||||
) -> &'ll Value {
|
||||
// Declare C ABI functions with the visibility used by C by default.
|
||||
let visibility = Visibility::from_generic(self.tcx.sess.default_visibility());
|
||||
|
||||
declare_raw_fn(self, name, llvm::CCallConv, unnamed, visibility, fn_type)
|
||||
// Visibility should always be default for declarations, otherwise the linker may report an
|
||||
// error.
|
||||
declare_raw_fn(self, name, llvm::CCallConv, unnamed, Visibility::Default, fn_type)
|
||||
}
|
||||
|
||||
/// Declare an entry Function
|
||||
|
|
|
@ -80,30 +80,6 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TargetFeatureDisableOrEnable<'a> {
|
||||
pub features: &'a [&'a str],
|
||||
pub span: Option<Span>,
|
||||
pub missing_features: Option<MissingFeatures>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[help(codegen_llvm_missing_features)]
|
||||
pub(crate) struct MissingFeatures;
|
||||
|
||||
impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
|
||||
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
|
||||
let mut diag = Diag::new(dcx, level, fluent::codegen_llvm_target_feature_disable_or_enable);
|
||||
if let Some(span) = self.span {
|
||||
diag.span(span);
|
||||
};
|
||||
if let Some(missing_features) = self.missing_features {
|
||||
diag.subdiagnostic(missing_features);
|
||||
}
|
||||
diag.arg("features", self.features.join(", "));
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_lto_disallowed)]
|
||||
pub(crate) struct LtoDisallowed;
|
||||
|
|
|
@ -86,6 +86,11 @@ fn get_simple_intrinsic<'ll>(
|
|||
sym::fmaf64 => "llvm.fma.f64",
|
||||
sym::fmaf128 => "llvm.fma.f128",
|
||||
|
||||
sym::fmuladdf16 => "llvm.fmuladd.f16",
|
||||
sym::fmuladdf32 => "llvm.fmuladd.f32",
|
||||
sym::fmuladdf64 => "llvm.fmuladd.f64",
|
||||
sym::fmuladdf128 => "llvm.fmuladd.f128",
|
||||
|
||||
sym::fabsf16 => "llvm.fabs.f16",
|
||||
sym::fabsf32 => "llvm.fabs.f32",
|
||||
sym::fabsf64 => "llvm.fabs.f64",
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::{ptr, slice, str};
|
|||
|
||||
use libc::c_int;
|
||||
use rustc_codegen_ssa::base::wants_wasm_eh;
|
||||
use rustc_codegen_ssa::codegen_attrs::check_tied_features;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::small_c_str::SmallCStr;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
|
@ -19,8 +20,8 @@ use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATU
|
|||
|
||||
use crate::back::write::create_informational_target_machine;
|
||||
use crate::errors::{
|
||||
FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable,
|
||||
UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
|
||||
FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, UnknownCTargetFeature,
|
||||
UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
|
||||
};
|
||||
use crate::llvm;
|
||||
|
||||
|
@ -247,7 +248,9 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
|
|||
("aarch64", "pmuv3") => Some(LLVMFeature::new("perfmon")),
|
||||
("aarch64", "paca") => Some(LLVMFeature::new("pauth")),
|
||||
("aarch64", "pacg") => Some(LLVMFeature::new("pauth")),
|
||||
("aarch64", "sve-b16b16") => Some(LLVMFeature::new("b16b16")),
|
||||
// Before LLVM 20 those two features were packaged together as b16b16
|
||||
("aarch64", "sve-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
|
||||
("aarch64", "sme-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
|
||||
("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")),
|
||||
// Rust ties fp and neon together.
|
||||
("aarch64", "neon") => {
|
||||
|
@ -276,25 +279,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
|
|||
}
|
||||
}
|
||||
|
||||
/// Given a map from target_features to whether they are enabled or disabled,
|
||||
/// ensure only valid combinations are allowed.
|
||||
pub(crate) fn check_tied_features(
|
||||
sess: &Session,
|
||||
features: &FxHashMap<&str, bool>,
|
||||
) -> Option<&'static [&'static str]> {
|
||||
if !features.is_empty() {
|
||||
for tied in sess.target.tied_target_features() {
|
||||
// Tied features must be set to the same value, or not set at all
|
||||
let mut tied_iter = tied.iter();
|
||||
let enabled = features.get(tied_iter.next().unwrap());
|
||||
if tied_iter.any(|f| enabled != features.get(f)) {
|
||||
return Some(tied);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Used to generate cfg variables and apply features
|
||||
/// Must express features in the way Rust understands them
|
||||
pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
|
||||
|
@ -685,7 +669,7 @@ pub(crate) fn global_llvm_features(
|
|||
features.extend(feats);
|
||||
|
||||
if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
|
||||
sess.dcx().emit_err(TargetFeatureDisableOrEnable {
|
||||
sess.dcx().emit_err(rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable {
|
||||
features: f,
|
||||
span: None,
|
||||
missing_features: None,
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use rustc_abi::Primitive::{Float, Int, Pointer};
|
||||
use rustc_abi::{Abi, Align, FieldsShape, Scalar, Size, Variants};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||
use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt};
|
||||
use rustc_target::abi::{Abi, Align, FieldsShape, Float, Int, Pointer, Scalar, Size, Variants};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::common::*;
|
||||
|
|
|
@ -14,6 +14,7 @@ itertools = "0.12"
|
|||
jobserver = "0.1.28"
|
||||
pathdiff = "0.2.0"
|
||||
regex = "1.4"
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
|
|
@ -183,6 +183,8 @@ codegen_ssa_metadata_object_file_write = error writing metadata object file: {$e
|
|||
|
||||
codegen_ssa_missing_cpp_build_tool_component = or a necessary component may be missing from the "C++ build tools" workload
|
||||
|
||||
codegen_ssa_missing_features = add the missing features in a `target_feature` attribute
|
||||
|
||||
codegen_ssa_missing_memory_ordering = Atomic intrinsic missing memory ordering
|
||||
|
||||
codegen_ssa_missing_query_depgraph =
|
||||
|
@ -238,6 +240,9 @@ codegen_ssa_stripping_debug_info_failed = stripping debug info with `{$util}` fa
|
|||
|
||||
codegen_ssa_symbol_file_write_failure = failed to write symbols file: {$error}
|
||||
|
||||
codegen_ssa_target_feature_disable_or_enable =
|
||||
the target features {$features} must all be either enabled or disabled together
|
||||
|
||||
codegen_ssa_target_feature_safe_trait = `#[target_feature(..)]` cannot be applied to safe trait method
|
||||
.label = cannot be applied to safe trait method
|
||||
.label_def = not an `unsafe` function
|
||||
|
|
|
@ -1087,7 +1087,9 @@ fn link_natively(
|
|||
let strip = sess.opts.cg.strip;
|
||||
|
||||
if sess.target.is_like_osx {
|
||||
let stripcmd = "/usr/bin/strip";
|
||||
// Use system `strip` when running on host macOS.
|
||||
// <https://github.com/rust-lang/rust/pull/130781>
|
||||
let stripcmd = if cfg!(target_os = "macos") { "/usr/bin/strip" } else { "strip" };
|
||||
match (strip, crate_type) {
|
||||
(Strip::Debuginfo, _) => {
|
||||
strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-S"))
|
||||
|
|
|
@ -404,12 +404,14 @@ impl<'a> GccLinker<'a> {
|
|||
fn build_dylib(&mut self, crate_type: CrateType, out_filename: &Path) {
|
||||
// On mac we need to tell the linker to let this library be rpathed
|
||||
if self.sess.target.is_like_osx {
|
||||
if !self.is_ld {
|
||||
if self.is_cc() {
|
||||
// `-dynamiclib` makes `cc` pass `-dylib` to the linker.
|
||||
self.cc_arg("-dynamiclib");
|
||||
} else {
|
||||
self.link_arg("-dylib");
|
||||
// Clang also sets `-dynamic`, but that's implied by `-dylib`, so unnecessary.
|
||||
}
|
||||
|
||||
self.link_arg("-dylib");
|
||||
|
||||
// Note that the `osx_rpath_install_name` option here is a hack
|
||||
// purely to support bootstrap right now, we should get a more
|
||||
// principled solution at some point to force the compiler to pass
|
||||
|
|
|
@ -2164,8 +2164,14 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool {
|
|||
&& tcx.sess.opts.cg.prefer_dynamic)
|
||||
);
|
||||
|
||||
// We need to generate _imp__ symbol if we are generating an rlib or we include one
|
||||
// indirectly from ThinLTO. In theory these are not needed as ThinLTO could resolve
|
||||
// these, but it currently does not do so.
|
||||
let can_have_static_objects =
|
||||
tcx.sess.lto() == Lto::Thin || tcx.crate_types().iter().any(|ct| *ct == CrateType::Rlib);
|
||||
|
||||
tcx.sess.target.is_like_windows &&
|
||||
tcx.crate_types().iter().any(|ct| *ct == CrateType::Rlib) &&
|
||||
can_have_static_objects &&
|
||||
// ThinLTO can't handle this workaround in all cases, so we don't
|
||||
// emit the `__imp_` symbols. Instead we make them unnecessary by disallowing
|
||||
// dynamic linking when linker plugin LTO is enabled.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use rustc_ast::{MetaItemInner, MetaItemKind, ast, attr};
|
||||
use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr, list_contains_name};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
|
||||
use rustc_hir as hir;
|
||||
|
@ -13,13 +14,13 @@ use rustc_middle::middle::codegen_fn_attrs::{
|
|||
use rustc_middle::mir::mono::Linkage;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self as ty, TyCtxt};
|
||||
use rustc_session::lint;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_session::{Session, lint};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_target::spec::{SanitizerSet, abi};
|
||||
|
||||
use crate::errors;
|
||||
use crate::errors::{self, MissingFeatures, TargetFeatureDisableOrEnable};
|
||||
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
|
||||
|
||||
fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
|
||||
|
@ -662,9 +663,49 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(features) = check_tied_features(
|
||||
tcx.sess,
|
||||
&codegen_fn_attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.map(|features| (features.name.as_str(), true))
|
||||
.collect(),
|
||||
) {
|
||||
let span = tcx
|
||||
.get_attrs(did, sym::target_feature)
|
||||
.next()
|
||||
.map_or_else(|| tcx.def_span(did), |a| a.span);
|
||||
tcx.dcx()
|
||||
.create_err(TargetFeatureDisableOrEnable {
|
||||
features,
|
||||
span: Some(span),
|
||||
missing_features: Some(MissingFeatures),
|
||||
})
|
||||
.emit();
|
||||
}
|
||||
|
||||
codegen_fn_attrs
|
||||
}
|
||||
|
||||
/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
|
||||
/// combinations are allowed.
|
||||
pub fn check_tied_features(
|
||||
sess: &Session,
|
||||
features: &FxHashMap<&str, bool>,
|
||||
) -> Option<&'static [&'static str]> {
|
||||
if !features.is_empty() {
|
||||
for tied in sess.target.tied_target_features() {
|
||||
// Tied features must be set to the same value, or not set at all
|
||||
let mut tied_iter = tied.iter();
|
||||
let enabled = features.get(tied_iter.next().unwrap());
|
||||
if tied_iter.any(|f| enabled != features.get(f)) {
|
||||
return Some(tied);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
|
||||
/// applied to the method prototype.
|
||||
fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_errors::codes::*;
|
|||
use rustc_errors::{
|
||||
Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
|
||||
};
|
||||
use rustc_macros::Diagnostic;
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::layout::LayoutError;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
@ -1068,3 +1068,27 @@ pub(crate) struct ErrorCreatingImportLibrary<'a> {
|
|||
pub lib_name: &'a str,
|
||||
pub error: String,
|
||||
}
|
||||
|
||||
pub struct TargetFeatureDisableOrEnable<'a> {
|
||||
pub features: &'a [&'a str],
|
||||
pub span: Option<Span>,
|
||||
pub missing_features: Option<MissingFeatures>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[help(codegen_ssa_missing_features)]
|
||||
pub struct MissingFeatures;
|
||||
|
||||
impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
|
||||
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
|
||||
let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable);
|
||||
if let Some(span) = self.span {
|
||||
diag.span(span);
|
||||
};
|
||||
if let Some(missing_features) = self.missing_features {
|
||||
diag.subdiagnostic(missing_features);
|
||||
}
|
||||
diag.arg("features", self.features.join(", "));
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
|
|
@ -992,10 +992,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
match op.val {
|
||||
Pair(data_ptr, meta) => {
|
||||
// In the case of Rc<Self>, we need to explicitly pass a
|
||||
// *mut RcBox<Self> with a Scalar (not ScalarPair) ABI. This is a hack
|
||||
// *mut RcInner<Self> with a Scalar (not ScalarPair) ABI. This is a hack
|
||||
// that is understood elsewhere in the compiler as a method on
|
||||
// `dyn Trait`.
|
||||
// To get a `*mut RcBox<Self>`, we just keep unwrapping newtypes until
|
||||
// To get a `*mut RcInner<Self>`, we just keep unwrapping newtypes until
|
||||
// we get a value of a built-in pointer type.
|
||||
//
|
||||
// This is also relevant for `Pin<&mut Self>`, where we need to peel the
|
||||
|
|
|
@ -3,12 +3,13 @@ use std::fmt;
|
|||
|
||||
use arrayvec::ArrayVec;
|
||||
use either::Either;
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::{Abi, Align, Size};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range};
|
||||
use rustc_middle::mir::{self, ConstValue};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_target::abi::{self, Abi, Align, Size};
|
||||
use tracing::debug;
|
||||
|
||||
use super::place::{PlaceRef, PlaceValue};
|
||||
|
@ -207,7 +208,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
match alloc.0.read_scalar(
|
||||
bx,
|
||||
alloc_range(start, size),
|
||||
/*read_provenance*/ matches!(s.primitive(), abi::Pointer(_)),
|
||||
/*read_provenance*/ matches!(s.primitive(), abi::Primitive::Pointer(_)),
|
||||
) {
|
||||
Ok(val) => bx.scalar_to_backend(val, s, ty),
|
||||
Err(_) => bx.const_poison(ty),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use rustc_abi::Primitive::{Int, Pointer};
|
||||
use rustc_abi::{Align, FieldsShape, Size, TagEncoding, Variants};
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_target::abi::{
|
||||
Align, FieldsShape, Int, Pointer, Size, TagEncoding, VariantIdx, Variants,
|
||||
};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::operand::OperandValue;
|
||||
|
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
either = "1"
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_apfloat = "0.2.0"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
|
|
@ -134,14 +134,16 @@ const_eval_incompatible_return_types =
|
|||
const_eval_incompatible_types =
|
||||
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
|
||||
|
||||
const_eval_interior_mutable_data_refer =
|
||||
const_eval_interior_mutable_ref_escaping =
|
||||
{const_eval_const_context}s cannot refer to interior mutable data
|
||||
.label = this borrow of an interior mutable value may end up in the final value
|
||||
.help = to fix this, the value can be extracted to a separate `static` item and then referenced
|
||||
.teach_note =
|
||||
A constant containing interior mutable data behind a reference can allow you to modify that data.
|
||||
This would make multiple uses of a constant to be able to see different values and allow circumventing
|
||||
the `Send` and `Sync` requirements for shared mutable data, which is unsound.
|
||||
References that escape into the final value of a constant or static must be immutable.
|
||||
This is to avoid accidentally creating shared mutable state.
|
||||
|
||||
|
||||
If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
|
||||
|
||||
const_eval_intern_kind = {$kind ->
|
||||
[static] static
|
||||
|
@ -229,6 +231,24 @@ const_eval_modified_global =
|
|||
|
||||
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
|
||||
|
||||
const_eval_mutable_raw_escaping =
|
||||
raw mutable pointers are not allowed in the final value of {const_eval_const_context}s
|
||||
.teach_note =
|
||||
Pointers that escape into the final value of a constant or static must be immutable.
|
||||
This is to avoid accidentally creating shared mutable state.
|
||||
|
||||
|
||||
If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
|
||||
|
||||
const_eval_mutable_ref_escaping =
|
||||
mutable references are not allowed in the final value of {const_eval_const_context}s
|
||||
.teach_note =
|
||||
References that escape into the final value of a constant or static must be immutable.
|
||||
This is to avoid accidentally creating shared mutable state.
|
||||
|
||||
|
||||
If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
|
||||
|
||||
const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
|
||||
const_eval_non_const_fmt_macro_call =
|
||||
cannot call non-const formatting macro in {const_eval_const_context}s
|
||||
|
@ -364,30 +384,11 @@ const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in
|
|||
const_eval_unallowed_heap_allocations =
|
||||
allocations are not allowed in {const_eval_const_context}s
|
||||
.label = allocation not allowed in {const_eval_const_context}s
|
||||
.teach_note = The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time.
|
||||
.teach_note =
|
||||
The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created.
|
||||
|
||||
const_eval_unallowed_inline_asm =
|
||||
inline assembly is not allowed in {const_eval_const_context}s
|
||||
const_eval_unallowed_mutable_raw =
|
||||
raw mutable pointers are not allowed in the final value of {const_eval_const_context}s
|
||||
.teach_note =
|
||||
References in statics and constants may only refer to immutable values.
|
||||
|
||||
|
||||
Statics are shared everywhere, and if they refer to mutable data one might violate memory
|
||||
safety since holding multiple mutable references to shared data is not allowed.
|
||||
|
||||
|
||||
If you really want global mutable state, try using static mut or a global UnsafeCell.
|
||||
|
||||
const_eval_unallowed_mutable_refs =
|
||||
mutable references are not allowed in the final value of {const_eval_const_context}s
|
||||
.teach_note =
|
||||
Statics are shared everywhere, and if they refer to mutable data one might violate memory
|
||||
safety since holding multiple mutable references to shared data is not allowed.
|
||||
|
||||
|
||||
If you really want global mutable state, try using static mut or a global UnsafeCell.
|
||||
|
||||
const_eval_unallowed_op_in_const_context =
|
||||
{$msg}
|
||||
|
|
|
@ -666,6 +666,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// This can be called on stable via the `vec!` macro.
|
||||
if tcx.is_lang_item(callee, LangItem::ExchangeMalloc) {
|
||||
self.check_op(ops::HeapAllocation);
|
||||
return;
|
||||
|
|
|
@ -402,7 +402,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
|
|||
DiagImportance::Secondary
|
||||
}
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
ccx.dcx().create_err(errors::InteriorMutableDataRefer {
|
||||
ccx.dcx().create_err(errors::InteriorMutableRefEscaping {
|
||||
span,
|
||||
opt_help: matches!(ccx.const_kind(), hir::ConstContext::Static(_)),
|
||||
kind: ccx.const_kind(),
|
||||
|
@ -430,12 +430,12 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
|
|||
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
match self.0 {
|
||||
hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::UnallowedMutableRaw {
|
||||
hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::MutableRawEscaping {
|
||||
span,
|
||||
kind: ccx.const_kind(),
|
||||
teach: ccx.tcx.sess.teach(E0764),
|
||||
}),
|
||||
hir::BorrowKind::Ref => ccx.dcx().create_err(errors::UnallowedMutableRefs {
|
||||
hir::BorrowKind::Ref => ccx.dcx().create_err(errors::MutableRefEscaping {
|
||||
span,
|
||||
kind: ccx.const_kind(),
|
||||
teach: ccx.tcx.sess.teach(E0764),
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_middle::mir::{
|
|||
self, BasicBlock, CallReturnPlaces, Local, Location, Statement, StatementKind, TerminatorEdges,
|
||||
};
|
||||
use rustc_mir_dataflow::fmt::DebugWithContext;
|
||||
use rustc_mir_dataflow::{Analysis, AnalysisDomain, JoinSemiLattice};
|
||||
use rustc_mir_dataflow::{Analysis, JoinSemiLattice};
|
||||
|
||||
use super::{ConstCx, Qualif, qualifs};
|
||||
|
||||
|
@ -310,7 +310,7 @@ impl JoinSemiLattice for State {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Q> AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
|
||||
impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
|
||||
where
|
||||
Q: Qualif,
|
||||
{
|
||||
|
@ -328,12 +328,7 @@ where
|
|||
fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) {
|
||||
self.transfer_function(state).initialize_state();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
|
||||
where
|
||||
Q: Qualif,
|
||||
{
|
||||
fn apply_statement_effect(
|
||||
&mut self,
|
||||
state: &mut Self::Domain,
|
||||
|
|
|
@ -140,7 +140,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> {
|
|||
|
||||
#[inline(always)]
|
||||
fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> {
|
||||
self.iter().filter_map(move |(k, v)| f(k, &*v)).collect()
|
||||
self.iter().filter_map(move |(k, v)| f(k, v)).collect()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -118,8 +118,8 @@ pub(crate) struct UnstableConstFn {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_unallowed_mutable_refs, code = E0764)]
|
||||
pub(crate) struct UnallowedMutableRefs {
|
||||
#[diag(const_eval_mutable_ref_escaping, code = E0764)]
|
||||
pub(crate) struct MutableRefEscaping {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub kind: ConstContext,
|
||||
|
@ -128,8 +128,8 @@ pub(crate) struct UnallowedMutableRefs {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_unallowed_mutable_raw, code = E0764)]
|
||||
pub(crate) struct UnallowedMutableRaw {
|
||||
#[diag(const_eval_mutable_raw_escaping, code = E0764)]
|
||||
pub(crate) struct MutableRawEscaping {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub kind: ConstContext,
|
||||
|
@ -181,8 +181,8 @@ pub(crate) struct UnallowedInlineAsm {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_interior_mutable_data_refer, code = E0492)]
|
||||
pub(crate) struct InteriorMutableDataRefer {
|
||||
#[diag(const_eval_interior_mutable_ref_escaping, code = E0492)]
|
||||
pub(crate) struct InteriorMutableRefEscaping {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
|
|
|
@ -334,19 +334,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
{
|
||||
use rustc_type_ir::TyKind::*;
|
||||
|
||||
fn adjust_nan<
|
||||
'tcx,
|
||||
M: Machine<'tcx>,
|
||||
F1: rustc_apfloat::Float + FloatConvert<F2>,
|
||||
F2: rustc_apfloat::Float,
|
||||
>(
|
||||
ecx: &InterpCx<'tcx, M>,
|
||||
f1: F1,
|
||||
f2: F2,
|
||||
) -> F2 {
|
||||
if f2.is_nan() { M::generate_nan(ecx, &[f1]) } else { f2 }
|
||||
}
|
||||
|
||||
match *dest_ty.kind() {
|
||||
// float -> uint
|
||||
Uint(t) => {
|
||||
|
@ -367,11 +354,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
// float -> float
|
||||
Float(fty) => match fty {
|
||||
FloatTy::F16 => Scalar::from_f16(adjust_nan(self, f, f.convert(&mut false).value)),
|
||||
FloatTy::F32 => Scalar::from_f32(adjust_nan(self, f, f.convert(&mut false).value)),
|
||||
FloatTy::F64 => Scalar::from_f64(adjust_nan(self, f, f.convert(&mut false).value)),
|
||||
FloatTy::F16 => {
|
||||
Scalar::from_f16(self.adjust_nan(f.convert(&mut false).value, &[f]))
|
||||
}
|
||||
FloatTy::F32 => {
|
||||
Scalar::from_f32(self.adjust_nan(f.convert(&mut false).value, &[f]))
|
||||
}
|
||||
FloatTy::F64 => {
|
||||
Scalar::from_f64(self.adjust_nan(f.convert(&mut false).value, &[f]))
|
||||
}
|
||||
FloatTy::F128 => {
|
||||
Scalar::from_f128(adjust_nan(self, f, f.convert(&mut false).value))
|
||||
Scalar::from_f128(self.adjust_nan(f.convert(&mut false).value, &[f]))
|
||||
}
|
||||
},
|
||||
// That's it.
|
||||
|
|
|
@ -599,6 +599,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
pub fn generate_stacktrace(&self) -> Vec<FrameInfo<'tcx>> {
|
||||
Frame::generate_stacktrace_from_stack(self.stack())
|
||||
}
|
||||
|
||||
pub fn adjust_nan<F1, F2>(&self, f: F2, inputs: &[F1]) -> F2
|
||||
where
|
||||
F1: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F2>,
|
||||
F2: rustc_apfloat::Float,
|
||||
{
|
||||
if f.is_nan() { M::generate_nan(self, inputs) } else { f }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use std::assert_matches::assert_matches;
|
||||
|
||||
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
|
||||
use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement};
|
||||
|
@ -438,6 +439,26 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
|
||||
}
|
||||
|
||||
sym::minnumf16 => self.float_min_intrinsic::<Half>(args, dest)?,
|
||||
sym::minnumf32 => self.float_min_intrinsic::<Single>(args, dest)?,
|
||||
sym::minnumf64 => self.float_min_intrinsic::<Double>(args, dest)?,
|
||||
sym::minnumf128 => self.float_min_intrinsic::<Quad>(args, dest)?,
|
||||
|
||||
sym::maxnumf16 => self.float_max_intrinsic::<Half>(args, dest)?,
|
||||
sym::maxnumf32 => self.float_max_intrinsic::<Single>(args, dest)?,
|
||||
sym::maxnumf64 => self.float_max_intrinsic::<Double>(args, dest)?,
|
||||
sym::maxnumf128 => self.float_max_intrinsic::<Quad>(args, dest)?,
|
||||
|
||||
sym::copysignf16 => self.float_copysign_intrinsic::<Half>(args, dest)?,
|
||||
sym::copysignf32 => self.float_copysign_intrinsic::<Single>(args, dest)?,
|
||||
sym::copysignf64 => self.float_copysign_intrinsic::<Double>(args, dest)?,
|
||||
sym::copysignf128 => self.float_copysign_intrinsic::<Quad>(args, dest)?,
|
||||
|
||||
sym::fabsf16 => self.float_abs_intrinsic::<Half>(args, dest)?,
|
||||
sym::fabsf32 => self.float_abs_intrinsic::<Single>(args, dest)?,
|
||||
sym::fabsf64 => self.float_abs_intrinsic::<Double>(args, dest)?,
|
||||
sym::fabsf128 => self.float_abs_intrinsic::<Quad>(args, dest)?,
|
||||
|
||||
// Unsupported intrinsic: skip the return_to_block below.
|
||||
_ => return interp_ok(false),
|
||||
}
|
||||
|
@ -697,4 +718,63 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let rhs_bytes = get_bytes(self, rhs)?;
|
||||
interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
|
||||
}
|
||||
|
||||
fn float_min_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
dest: &MPlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let a: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let b: F = self.read_scalar(&args[1])?.to_float()?;
|
||||
let res = self.adjust_nan(a.min(b), &[a, b]);
|
||||
self.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn float_max_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
dest: &MPlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let a: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let b: F = self.read_scalar(&args[1])?.to_float()?;
|
||||
let res = self.adjust_nan(a.max(b), &[a, b]);
|
||||
self.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn float_copysign_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
dest: &MPlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let a: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let b: F = self.read_scalar(&args[1])?.to_float()?;
|
||||
// bitwise, no NaN adjustments
|
||||
self.write_scalar(a.copy_sign(b), dest)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn float_abs_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
dest: &MPlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let x: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
// bitwise, no NaN adjustments
|
||||
self.write_scalar(x.abs(), dest)?;
|
||||
interp_ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -993,11 +993,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
bytes
|
||||
}
|
||||
|
||||
/// Find leaked allocations. Allocations reachable from `static_roots` or a `Global` allocation
|
||||
/// are not considered leaked, as well as leaks whose kind's `may_leak()` returns true.
|
||||
pub fn find_leaked_allocations(
|
||||
&self,
|
||||
static_roots: &[AllocId],
|
||||
/// Find leaked allocations, remove them from memory and return them. Allocations reachable from
|
||||
/// `static_roots` or a `Global` allocation are not considered leaked, as well as leaks whose
|
||||
/// kind's `may_leak()` returns true.
|
||||
///
|
||||
/// This is highly destructive, no more execution can happen after this!
|
||||
pub fn take_leaked_allocations(
|
||||
&mut self,
|
||||
static_roots: impl FnOnce(&Self) -> &[AllocId],
|
||||
) -> Vec<(AllocId, MemoryKind<M::MemoryKind>, Allocation<M::Provenance, M::AllocExtra, M::Bytes>)>
|
||||
{
|
||||
// Collect the set of allocations that are *reachable* from `Global` allocations.
|
||||
|
@ -1008,7 +1011,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.memory.alloc_map.filter_map_collect(move |&id, &(kind, _)| {
|
||||
if Some(kind) == global_kind { Some(id) } else { None }
|
||||
});
|
||||
todo.extend(static_roots);
|
||||
todo.extend(static_roots(self));
|
||||
while let Some(id) = todo.pop() {
|
||||
if reachable.insert(id) {
|
||||
// This is a new allocation, add the allocation it points to `todo`.
|
||||
|
@ -1023,13 +1026,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
};
|
||||
|
||||
// All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking.
|
||||
self.memory.alloc_map.filter_map_collect(|id, (kind, alloc)| {
|
||||
if kind.may_leak() || reachable.contains(id) {
|
||||
None
|
||||
} else {
|
||||
Some((*id, *kind, alloc.clone()))
|
||||
}
|
||||
})
|
||||
let leaked: Vec<_> = self.memory.alloc_map.filter_map_collect(|&id, &(kind, _)| {
|
||||
if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) }
|
||||
});
|
||||
let mut result = Vec::new();
|
||||
for &id in leaked.iter() {
|
||||
let (kind, alloc) = self.memory.alloc_map.remove(&id).unwrap();
|
||||
result.push((id, kind, alloc));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Runs the closure in "validation" mode, which means the machine's memory read hooks will be
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
|
||||
use either::{Either, Left, Right};
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::{Abi, HasDataLayout, Size};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_middle::mir::interpret::ScalarSizeMismatch;
|
||||
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
|
||||
use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, mir, span_bug, ty};
|
||||
use rustc_target::abi::{self, Abi, HasDataLayout, Size};
|
||||
use tracing::trace;
|
||||
|
||||
use super::{
|
||||
|
@ -117,7 +118,7 @@ impl<Prov: Provenance> Immediate<Prov> {
|
|||
match (self, abi) {
|
||||
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
|
||||
assert_eq!(scalar.size(), s.size(cx), "{msg}: scalar value has wrong size");
|
||||
if !matches!(s.primitive(), abi::Pointer(..)) {
|
||||
if !matches!(s.primitive(), abi::Primitive::Pointer(..)) {
|
||||
// This is not a pointer, it should not carry provenance.
|
||||
assert!(
|
||||
matches!(scalar, Scalar::Int(..)),
|
||||
|
@ -131,7 +132,7 @@ impl<Prov: Provenance> Immediate<Prov> {
|
|||
a.size(cx),
|
||||
"{msg}: first component of scalar pair has wrong size"
|
||||
);
|
||||
if !matches!(a.primitive(), abi::Pointer(..)) {
|
||||
if !matches!(a.primitive(), abi::Primitive::Pointer(..)) {
|
||||
assert!(
|
||||
matches!(a_val, Scalar::Int(..)),
|
||||
"{msg}: first component of scalar pair should be an integer, but has provenance"
|
||||
|
@ -142,7 +143,7 @@ impl<Prov: Provenance> Immediate<Prov> {
|
|||
b.size(cx),
|
||||
"{msg}: second component of scalar pair has wrong size"
|
||||
);
|
||||
if !matches!(b.primitive(), abi::Pointer(..)) {
|
||||
if !matches!(b.primitive(), abi::Primitive::Pointer(..)) {
|
||||
assert!(
|
||||
matches!(b_val, Scalar::Int(..)),
|
||||
"{msg}: second component of scalar pair should be an integer, but has provenance"
|
||||
|
@ -572,7 +573,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
|
||||
let scalar = alloc.read_scalar(
|
||||
alloc_range(Size::ZERO, size),
|
||||
/*read_provenance*/ matches!(s, abi::Pointer(_)),
|
||||
/*read_provenance*/ matches!(s, abi::Primitive::Pointer(_)),
|
||||
)?;
|
||||
Some(ImmTy::from_scalar(scalar, mplace.layout))
|
||||
}
|
||||
|
@ -588,11 +589,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
|
||||
let a_val = alloc.read_scalar(
|
||||
alloc_range(Size::ZERO, a_size),
|
||||
/*read_provenance*/ matches!(a, abi::Pointer(_)),
|
||||
/*read_provenance*/ matches!(a, abi::Primitive::Pointer(_)),
|
||||
)?;
|
||||
let b_val = alloc.read_scalar(
|
||||
alloc_range(b_offset, b_size),
|
||||
/*read_provenance*/ matches!(b, abi::Pointer(_)),
|
||||
/*read_provenance*/ matches!(b, abi::Primitive::Pointer(_)),
|
||||
)?;
|
||||
Some(ImmTy::from_immediate(Immediate::ScalarPair(a_val, b_val), mplace.layout))
|
||||
}
|
||||
|
|
|
@ -64,8 +64,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
use rustc_middle::mir::BinOp::*;
|
||||
|
||||
// Performs appropriate non-deterministic adjustments of NaN results.
|
||||
let adjust_nan =
|
||||
|f: F| -> F { if f.is_nan() { M::generate_nan(self, &[l, r]) } else { f } };
|
||||
let adjust_nan = |f: F| -> F { self.adjust_nan(f, &[l, r]) };
|
||||
|
||||
match bin_op {
|
||||
Eq => ImmTy::from_bool(l == r, *self.tcx),
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![cfg_attr(not(bootstrap), feature(unqualified_local_imports))]
|
||||
#![cfg_attr(not(bootstrap), warn(unqualified_local_imports))]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
|
@ -15,7 +13,9 @@
|
|||
#![feature(strict_provenance)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(unqualified_local_imports)]
|
||||
#![feature(yeet_expr)]
|
||||
#![warn(unqualified_local_imports)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ pub mod svh;
|
|||
pub mod sync;
|
||||
pub mod tagged_ptr;
|
||||
pub mod temp_dir;
|
||||
pub mod thinvec;
|
||||
pub mod transitive_relation;
|
||||
pub mod unhash;
|
||||
pub mod unord;
|
||||
|
|
|
@ -75,6 +75,7 @@ use std::fmt::Debug;
|
|||
use std::hash;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use thin_vec::ThinVec;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::fx::{FxHashMap, FxHashSet};
|
||||
|
@ -141,7 +142,7 @@ pub trait ObligationProcessor {
|
|||
#[derive(Debug)]
|
||||
pub enum ProcessResult<O, E> {
|
||||
Unchanged,
|
||||
Changed(Vec<O>),
|
||||
Changed(ThinVec<O>),
|
||||
Error(E),
|
||||
}
|
||||
|
||||
|
@ -402,9 +403,10 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
}
|
||||
|
||||
/// Returns the set of obligations that are in a pending state.
|
||||
pub fn map_pending_obligations<P, F>(&self, f: F) -> Vec<P>
|
||||
pub fn map_pending_obligations<P, F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: Fn(&O) -> P,
|
||||
R: FromIterator<P>,
|
||||
{
|
||||
self.nodes
|
||||
.iter()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::fmt;
|
||||
|
||||
use thin_vec::thin_vec;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'a> super::ForestObligation for &'a str {
|
||||
|
@ -101,9 +103,9 @@ fn push_pop() {
|
|||
// |-> A.3
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
|
||||
"A" => ProcessResult::Changed(thin_vec!["A.1", "A.2", "A.3"]),
|
||||
"B" => ProcessResult::Error("B is for broken"),
|
||||
"C" => ProcessResult::Changed(vec![]),
|
||||
"C" => ProcessResult::Changed(thin_vec![]),
|
||||
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -123,8 +125,8 @@ fn push_pop() {
|
|||
|obligation| match *obligation {
|
||||
"A.1" => ProcessResult::Unchanged,
|
||||
"A.2" => ProcessResult::Unchanged,
|
||||
"A.3" => ProcessResult::Changed(vec!["A.3.i"]),
|
||||
"D" => ProcessResult::Changed(vec!["D.1", "D.2"]),
|
||||
"A.3" => ProcessResult::Changed(thin_vec!["A.3.i"]),
|
||||
"D" => ProcessResult::Changed(thin_vec!["D.1", "D.2"]),
|
||||
"A.3.i" | "D.1" | "D.2" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -139,11 +141,11 @@ fn push_pop() {
|
|||
// |-> D.2 |-> D.2.i
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.1" => ProcessResult::Changed(vec![]),
|
||||
"A.1" => ProcessResult::Changed(thin_vec![]),
|
||||
"A.2" => ProcessResult::Error("A is for apple"),
|
||||
"A.3.i" => ProcessResult::Changed(vec![]),
|
||||
"D.1" => ProcessResult::Changed(vec!["D.1.i"]),
|
||||
"D.2" => ProcessResult::Changed(vec!["D.2.i"]),
|
||||
"A.3.i" => ProcessResult::Changed(thin_vec![]),
|
||||
"D.1" => ProcessResult::Changed(thin_vec!["D.1.i"]),
|
||||
"D.2" => ProcessResult::Changed(thin_vec!["D.2.i"]),
|
||||
"D.1.i" | "D.2.i" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -158,7 +160,7 @@ fn push_pop() {
|
|||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"D.1.i" => ProcessResult::Error("D is for dumb"),
|
||||
"D.2.i" => ProcessResult::Changed(vec![]),
|
||||
"D.2.i" => ProcessResult::Changed(thin_vec![]),
|
||||
_ => panic!("unexpected obligation {:?}", obligation),
|
||||
},
|
||||
|_| {},
|
||||
|
@ -184,10 +186,10 @@ fn success_in_grandchildren() {
|
|||
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
|
||||
"A.1" => ProcessResult::Changed(vec![]),
|
||||
"A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]),
|
||||
"A.3" => ProcessResult::Changed(vec![]),
|
||||
"A" => ProcessResult::Changed(thin_vec!["A.1", "A.2", "A.3"]),
|
||||
"A.1" => ProcessResult::Changed(thin_vec![]),
|
||||
"A.2" => ProcessResult::Changed(thin_vec!["A.2.i", "A.2.ii"]),
|
||||
"A.3" => ProcessResult::Changed(thin_vec![]),
|
||||
"A.2.i" | "A.2.ii" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -201,7 +203,7 @@ fn success_in_grandchildren() {
|
|||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.2.i" => ProcessResult::Unchanged,
|
||||
"A.2.ii" => ProcessResult::Changed(vec![]),
|
||||
"A.2.ii" => ProcessResult::Changed(thin_vec![]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
|
@ -211,7 +213,7 @@ fn success_in_grandchildren() {
|
|||
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]),
|
||||
"A.2.i" => ProcessResult::Changed(thin_vec!["A.2.i.a"]),
|
||||
"A.2.i.a" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -222,7 +224,7 @@ fn success_in_grandchildren() {
|
|||
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.2.i.a" => ProcessResult::Changed(vec![]),
|
||||
"A.2.i.a" => ProcessResult::Changed(thin_vec![]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
|
@ -247,7 +249,7 @@ fn to_errors_no_throw() {
|
|||
forest.register_obligation("A");
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
|
||||
"A" => ProcessResult::Changed(thin_vec!["A.1", "A.2", "A.3"]),
|
||||
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -269,7 +271,7 @@ fn diamond() {
|
|||
forest.register_obligation("A");
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["A.1", "A.2"]),
|
||||
"A" => ProcessResult::Changed(thin_vec!["A.1", "A.2"]),
|
||||
"A.1" | "A.2" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -280,8 +282,8 @@ fn diamond() {
|
|||
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.1" => ProcessResult::Changed(vec!["D"]),
|
||||
"A.2" => ProcessResult::Changed(vec!["D"]),
|
||||
"A.1" => ProcessResult::Changed(thin_vec!["D"]),
|
||||
"A.2" => ProcessResult::Changed(thin_vec!["D"]),
|
||||
"D" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -295,7 +297,7 @@ fn diamond() {
|
|||
|obligation| match *obligation {
|
||||
"D" => {
|
||||
d_count += 1;
|
||||
ProcessResult::Changed(vec![])
|
||||
ProcessResult::Changed(thin_vec![])
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -313,7 +315,7 @@ fn diamond() {
|
|||
forest.register_obligation("A'");
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]),
|
||||
"A'" => ProcessResult::Changed(thin_vec!["A'.1", "A'.2"]),
|
||||
"A'.1" | "A'.2" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -324,8 +326,8 @@ fn diamond() {
|
|||
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A'.1" => ProcessResult::Changed(vec!["D'", "A'"]),
|
||||
"A'.2" => ProcessResult::Changed(vec!["D'"]),
|
||||
"A'.1" => ProcessResult::Changed(thin_vec!["D'", "A'"]),
|
||||
"A'.2" => ProcessResult::Changed(thin_vec!["D'"]),
|
||||
"D'" | "A'" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -366,7 +368,7 @@ fn done_dependency() {
|
|||
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]),
|
||||
"A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(thin_vec![]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
|
@ -379,7 +381,9 @@ fn done_dependency() {
|
|||
forest.register_obligation("(A,B,C): Sized");
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"(A,B,C): Sized" => ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"]),
|
||||
"(A,B,C): Sized" => {
|
||||
ProcessResult::Changed(thin_vec!["A: Sized", "B: Sized", "C: Sized"])
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
|
@ -399,10 +403,10 @@ fn orphan() {
|
|||
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["D", "E"]),
|
||||
"A" => ProcessResult::Changed(thin_vec!["D", "E"]),
|
||||
"B" => ProcessResult::Unchanged,
|
||||
"C1" => ProcessResult::Changed(vec![]),
|
||||
"C2" => ProcessResult::Changed(vec![]),
|
||||
"C1" => ProcessResult::Changed(thin_vec![]),
|
||||
"C2" => ProcessResult::Changed(thin_vec![]),
|
||||
"D" | "E" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -416,7 +420,7 @@ fn orphan() {
|
|||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"D" | "E" => ProcessResult::Unchanged,
|
||||
"B" => ProcessResult::Changed(vec!["D"]),
|
||||
"B" => ProcessResult::Changed(thin_vec!["D"]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
|
@ -459,7 +463,7 @@ fn simultaneous_register_and_error() {
|
|||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Error("An error"),
|
||||
"B" => ProcessResult::Changed(vec!["A"]),
|
||||
"B" => ProcessResult::Changed(thin_vec!["A"]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
|
@ -474,7 +478,7 @@ fn simultaneous_register_and_error() {
|
|||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Error("An error"),
|
||||
"B" => ProcessResult::Changed(vec!["A"]),
|
||||
"B" => ProcessResult::Changed(thin_vec!["A"]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
|
|
|
@ -57,7 +57,7 @@ impl<T> Steal<T> {
|
|||
///
|
||||
/// This should not be used within rustc as it leaks information not tracked
|
||||
/// by the query system, breaking incremental compilation.
|
||||
#[cfg_attr(not(bootstrap), rustc_lint_untracked_query_information)]
|
||||
#[rustc_lint_untracked_query_information]
|
||||
pub fn is_stolen(&self) -> bool {
|
||||
self.value.borrow().is_none()
|
||||
}
|
||||
|
|
92
compiler/rustc_data_structures/src/thinvec.rs
Normal file
92
compiler/rustc_data_structures/src/thinvec.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
//! This is a copy-paste of `Vec::extract_if` for `ThinVec`.
|
||||
//!
|
||||
//! FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed.
|
||||
|
||||
use std::{ptr, slice};
|
||||
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
/// An iterator for [`ThinVec`] which uses a closure to determine if an element should be removed.
|
||||
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||
pub struct ExtractIf<'a, T, F> {
|
||||
vec: &'a mut ThinVec<T>,
|
||||
/// The index of the item that will be inspected by the next call to `next`.
|
||||
idx: usize,
|
||||
/// The number of items that have been drained (removed) thus far.
|
||||
del: usize,
|
||||
/// The original length of `vec` prior to draining.
|
||||
old_len: usize,
|
||||
/// The filter test predicate.
|
||||
pred: F,
|
||||
}
|
||||
|
||||
impl<'a, T, F> ExtractIf<'a, T, F>
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
pub fn new(vec: &'a mut ThinVec<T>, filter: F) -> Self {
|
||||
let old_len = vec.len();
|
||||
|
||||
// Guard against us getting leaked (leak amplification)
|
||||
unsafe {
|
||||
vec.set_len(0);
|
||||
}
|
||||
|
||||
ExtractIf { vec, idx: 0, del: 0, old_len, pred: filter }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F> Iterator for ExtractIf<'_, T, F>
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
type Item = T;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
while self.idx < self.old_len {
|
||||
let i = self.idx;
|
||||
let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
|
||||
let drained = (self.pred)(&mut v[i]);
|
||||
// Update the index *after* the predicate is called. If the index
|
||||
// is updated prior and the predicate panics, the element at this
|
||||
// index would be leaked.
|
||||
self.idx += 1;
|
||||
if drained {
|
||||
self.del += 1;
|
||||
return Some(ptr::read(&v[i]));
|
||||
} else if self.del > 0 {
|
||||
let del = self.del;
|
||||
let src: *const T = &v[i];
|
||||
let dst: *mut T = &mut v[i - del];
|
||||
ptr::copy_nonoverlapping(src, dst, 1);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(0, Some(self.old_len - self.idx))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F> Drop for ExtractIf<'_, A, F> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.idx < self.old_len && self.del > 0 {
|
||||
// This is a pretty messed up state, and there isn't really an
|
||||
// obviously right thing to do. We don't want to keep trying
|
||||
// to execute `pred`, so we just backshift all the unprocessed
|
||||
// elements and tell the vec that they still exist. The backshift
|
||||
// is required to prevent a double-drop of the last successfully
|
||||
// drained item prior to a panic in the predicate.
|
||||
let ptr = self.vec.as_mut_ptr();
|
||||
let src = ptr.add(self.idx);
|
||||
let dst = src.sub(self.del);
|
||||
let tail_len = self.old_len - self.idx;
|
||||
src.copy_to(dst, tail_len);
|
||||
}
|
||||
self.vec.set_len(self.old_len - self.del);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue