Auto merge of #3975 - rust-lang:rustup-2024-10-17, r=RalfJung
Automatic Rustup
This commit is contained in:
commit
f7ccac9666
648 changed files with 9561 additions and 4426 deletions
1
.mailmap
1
.mailmap
|
|
@ -280,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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -3857,6 +3857,7 @@ dependencies = [
|
|||
"rustc_target",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
@ -4496,6 +4497,7 @@ dependencies = [
|
|||
"rustc_transmute",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
@ -4567,6 +4569,7 @@ dependencies = [
|
|||
"rustc_span",
|
||||
"rustc_type_ir_macros",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
|
|||
177
RELEASES.md
177
RELEASES.md
|
|
@ -1,3 +1,180 @@
|
|||
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::Waker::waker`](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)
|
||||
==========================
|
||||
|
||||
|
|
|
|||
|
|
@ -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,6 +2163,10 @@ 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 (`!`).
|
||||
|
|
@ -2501,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(
|
||||
|
|
@ -2965,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,
|
||||
|
||||
|
|
@ -2972,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);
|
||||
}
|
||||
|
|
@ -913,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 {
|
||||
|
|
@ -1034,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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -720,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)
|
||||
|
|
@ -1277,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 {
|
||||
|
|
@ -1326,10 +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) => {
|
||||
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() {
|
||||
|
|
@ -1845,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,
|
||||
}
|
||||
}),
|
||||
|
|
@ -1958,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))]
|
||||
|
|
@ -1982,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))
|
||||
}
|
||||
|
|
@ -2189,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> {
|
||||
|
|
@ -2634,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,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
|
||||
|
|
|
|||
|
|
@ -524,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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1260,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 });
|
||||
|
|
@ -1280,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 =>
|
||||
{
|
||||
|
|
@ -1321,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() {
|
||||
|
|
@ -1669,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)
|
||||
|
|
@ -1697,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)]
|
||||
|
|
|
|||
|
|
@ -546,6 +546,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
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) {
|
||||
|
|
@ -665,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("!");
|
||||
}
|
||||
|
|
@ -1253,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)
|
||||
}
|
||||
|
||||
|
|
@ -1740,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 {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -143,24 +143,25 @@ impl<'a> ExtCtxt<'a> {
|
|||
ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID }
|
||||
}
|
||||
|
||||
pub fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef {
|
||||
pub fn poly_trait_ref(&self, span: Span, path: ast::Path, is_const: bool) -> ast::PolyTraitRef {
|
||||
ast::PolyTraitRef {
|
||||
bound_generic_params: ThinVec::new(),
|
||||
modifiers: ast::TraitBoundModifiers {
|
||||
polarity: ast::BoundPolarity::Positive,
|
||||
constness: if is_const {
|
||||
ast::BoundConstness::Maybe(DUMMY_SP)
|
||||
} else {
|
||||
ast::BoundConstness::Never
|
||||
},
|
||||
asyncness: ast::BoundAsyncness::Normal,
|
||||
},
|
||||
trait_ref: self.trait_ref(path),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trait_bound(&self, path: ast::Path, is_const: bool) -> ast::GenericBound {
|
||||
ast::GenericBound::Trait(self.poly_trait_ref(path.span, path), ast::TraitBoundModifiers {
|
||||
polarity: ast::BoundPolarity::Positive,
|
||||
constness: if is_const {
|
||||
ast::BoundConstness::Maybe(DUMMY_SP)
|
||||
} else {
|
||||
ast::BoundConstness::Never
|
||||
},
|
||||
asyncness: ast::BoundAsyncness::Normal,
|
||||
})
|
||||
ast::GenericBound::Trait(self.poly_trait_ref(path.span, path, is_const))
|
||||
}
|
||||
|
||||
pub fn lifetime(&self, span: Span, ident: Ident) -> ast::Lifetime {
|
||||
|
|
@ -220,6 +221,10 @@ impl<'a> ExtCtxt<'a> {
|
|||
self.stmt_local(local, span)
|
||||
}
|
||||
|
||||
pub fn stmt_semi(&self, expr: P<ast::Expr>) -> ast::Stmt {
|
||||
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Semi(expr) }
|
||||
}
|
||||
|
||||
pub fn stmt_local(&self, local: P<ast::Local>, span: Span) -> ast::Stmt {
|
||||
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Let(local), span }
|
||||
}
|
||||
|
|
@ -287,6 +292,25 @@ impl<'a> ExtCtxt<'a> {
|
|||
self.expr(sp, ast::ExprKind::Paren(e))
|
||||
}
|
||||
|
||||
pub fn expr_method_call(
|
||||
&self,
|
||||
span: Span,
|
||||
expr: P<ast::Expr>,
|
||||
ident: Ident,
|
||||
args: ThinVec<P<ast::Expr>>,
|
||||
) -> P<ast::Expr> {
|
||||
let seg = ast::PathSegment::from_ident(ident);
|
||||
self.expr(
|
||||
span,
|
||||
ast::ExprKind::MethodCall(Box::new(ast::MethodCall {
|
||||
seg,
|
||||
receiver: expr,
|
||||
args,
|
||||
span,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn expr_call(
|
||||
&self,
|
||||
span: Span,
|
||||
|
|
@ -295,6 +319,12 @@ impl<'a> ExtCtxt<'a> {
|
|||
) -> P<ast::Expr> {
|
||||
self.expr(span, ast::ExprKind::Call(expr, args))
|
||||
}
|
||||
pub fn expr_loop(&self, sp: Span, block: P<ast::Block>) -> P<ast::Expr> {
|
||||
self.expr(sp, ast::ExprKind::Loop(block, None, sp))
|
||||
}
|
||||
pub fn expr_asm(&self, sp: Span, expr: P<ast::InlineAsm>) -> P<ast::Expr> {
|
||||
self.expr(sp, ast::ExprKind::InlineAsm(expr))
|
||||
}
|
||||
pub fn expr_call_ident(
|
||||
&self,
|
||||
span: Span,
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ declare_features! (
|
|||
/// Allows calling constructor functions in `const fn`.
|
||||
(accepted, const_constructor, "1.40.0", Some(61456)),
|
||||
/// Allows the definition of `const extern fn` and `const unsafe extern fn`.
|
||||
(accepted, const_extern_fn, "CURRENT_RUSTC_VERSION", Some(64926)),
|
||||
(accepted, const_extern_fn, "1.83.0", Some(64926)),
|
||||
/// Allows basic arithmetic on floating point types in a `const fn`.
|
||||
(accepted, const_fn_floating_point_arithmetic, "1.82.0", Some(57241)),
|
||||
/// Allows using and casting function pointers in a `const fn`.
|
||||
|
|
@ -144,15 +144,15 @@ declare_features! (
|
|||
/// Allows the use of `loop` and `while` in constants.
|
||||
(accepted, const_loop, "1.46.0", Some(52000)),
|
||||
/// Allows using `&mut` in constant functions.
|
||||
(accepted, const_mut_refs, "CURRENT_RUSTC_VERSION", Some(57349)),
|
||||
(accepted, const_mut_refs, "1.83.0", Some(57349)),
|
||||
/// Allows panicking during const eval (producing compile-time errors).
|
||||
(accepted, const_panic, "1.57.0", Some(51999)),
|
||||
/// Allows dereferencing raw pointers during const eval.
|
||||
(accepted, const_raw_ptr_deref, "1.58.0", Some(51911)),
|
||||
/// Allows references to types with interior mutability within constants
|
||||
(accepted, const_refs_to_cell, "CURRENT_RUSTC_VERSION", Some(80384)),
|
||||
(accepted, const_refs_to_cell, "1.83.0", Some(80384)),
|
||||
/// Allows creating pointers and references to `static` items in constants.
|
||||
(accepted, const_refs_to_static, "CURRENT_RUSTC_VERSION", Some(119618)),
|
||||
(accepted, const_refs_to_static, "1.83.0", Some(119618)),
|
||||
/// Allows implementing `Copy` for closures where possible (RFC 2132).
|
||||
(accepted, copy_closures, "1.26.0", Some(44490)),
|
||||
/// Allows `crate` in paths.
|
||||
|
|
@ -190,7 +190,7 @@ declare_features! (
|
|||
/// Allows explicit generic arguments specification with `impl Trait` present.
|
||||
(accepted, explicit_generic_args_with_impl_trait, "1.63.0", Some(83701)),
|
||||
/// Uses 2024 rules for matching `expr` fragments in macros. Also enables `expr_2021` fragment.
|
||||
(accepted, expr_fragment_specifier_2024, "CURRENT_RUSTC_VERSION", Some(123742)),
|
||||
(accepted, expr_fragment_specifier_2024, "1.83.0", Some(123742)),
|
||||
/// Allows arbitrary expressions in key-value attributes at parse time.
|
||||
(accepted, extended_key_value_attributes, "1.54.0", Some(78835)),
|
||||
/// Allows resolving absolute paths as paths from other crates.
|
||||
|
|
|
|||
|
|
@ -752,6 +752,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
template!(NameValueStr: "transparent|semitransparent|opaque"), ErrorFollowing,
|
||||
EncodeCrossCrate::Yes, "used internally for testing macro hygiene",
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_autodiff, Normal,
|
||||
template!(Word, List: r#""...""#), DuplicatesOk,
|
||||
EncodeCrossCrate::No, INTERNAL_UNSTABLE
|
||||
),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes, Diagnostics related:
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ declare_features! (
|
|||
(removed, no_stack_check, "1.0.0", None, None),
|
||||
/// Allows making `dyn Trait` well-formed even if `Trait` is not dyn-compatible (object safe).
|
||||
/// Renamed to `dyn_compatible_for_dispatch`.
|
||||
(removed, object_safe_for_dispatch, "CURRENT_RUSTC_VERSION", Some(43561),
|
||||
(removed, object_safe_for_dispatch, "1.83.0", Some(43561),
|
||||
Some("renamed to `dyn_compatible_for_dispatch`")),
|
||||
/// Allows using `#[on_unimplemented(..)]` on traits.
|
||||
/// (Moved to `rustc_attrs`.)
|
||||
|
|
@ -221,7 +221,7 @@ declare_features! (
|
|||
/// Allows using items which are missing stability attributes
|
||||
(removed, unmarked_api, "1.0.0", None, None),
|
||||
/// Allows unnamed fields of struct and union type
|
||||
(removed, unnamed_fields, "CURRENT_RUSTC_VERSION", Some(49804), Some("feature needs redesign")),
|
||||
(removed, unnamed_fields, "1.83.0", Some(49804), Some("feature needs redesign")),
|
||||
(removed, unsafe_no_drop_flag, "1.0.0", None, None),
|
||||
/// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue.
|
||||
(removed, untagged_unions, "1.13.0", Some(55149),
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ declare_features! (
|
|||
/// Added for testing unstable lints; perma-unstable.
|
||||
(internal, test_unstable_lint, "1.60.0", None),
|
||||
/// Helps with formatting for `group_imports = "StdExternalCrate"`.
|
||||
(unstable, unqualified_local_imports, "CURRENT_RUSTC_VERSION", None),
|
||||
(unstable, unqualified_local_imports, "1.83.0", None),
|
||||
/// Use for stable + negative coherence and strict coherence depending on trait's
|
||||
/// rustc_strict_coherence value.
|
||||
(unstable, with_negative_coherence, "1.60.0", None),
|
||||
|
|
@ -266,7 +266,7 @@ declare_features! (
|
|||
/// Renamed from `object_safe_for_dispatch`.
|
||||
///
|
||||
/// [^1]: Formerly known as "object safe".
|
||||
(unstable, dyn_compatible_for_dispatch, "CURRENT_RUSTC_VERSION", Some(43561)),
|
||||
(unstable, dyn_compatible_for_dispatch, "1.83.0", Some(43561)),
|
||||
/// Allows using the `#[fundamental]` attribute.
|
||||
(unstable, fundamental, "1.0.0", Some(29635)),
|
||||
/// Allows using `#[link_name="llvm.*"]`.
|
||||
|
|
@ -360,7 +360,7 @@ declare_features! (
|
|||
/// Allows inherent and trait methods with arbitrary self types.
|
||||
(unstable, arbitrary_self_types, "1.23.0", Some(44874)),
|
||||
/// Allows inherent and trait methods with arbitrary self types that are raw pointers.
|
||||
(unstable, arbitrary_self_types_pointers, "CURRENT_RUSTC_VERSION", Some(44874)),
|
||||
(unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)),
|
||||
/// Enables experimental inline assembly support for additional architectures.
|
||||
(unstable, asm_experimental_arch, "1.58.0", Some(93335)),
|
||||
/// Allows using `label` operands in inline assembly.
|
||||
|
|
@ -380,7 +380,7 @@ declare_features! (
|
|||
/// Allows using C-variadics.
|
||||
(unstable, c_variadic, "1.34.0", Some(44930)),
|
||||
/// Allows the use of `#[cfg(<true/false>)]`.
|
||||
(unstable, cfg_boolean_literals, "CURRENT_RUSTC_VERSION", Some(131204)),
|
||||
(unstable, cfg_boolean_literals, "1.83.0", Some(131204)),
|
||||
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
|
||||
(unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
|
||||
/// Provides the relocation model information as cfg entry
|
||||
|
|
@ -498,7 +498,7 @@ declare_features! (
|
|||
/// Allows `if let` guard in match arms.
|
||||
(unstable, if_let_guard, "1.47.0", Some(51114)),
|
||||
/// Rescoping temporaries in `if let` to align with Rust 2024.
|
||||
(unstable, if_let_rescope, "CURRENT_RUSTC_VERSION", Some(124085)),
|
||||
(unstable, if_let_rescope, "1.83.0", Some(124085)),
|
||||
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
|
||||
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
|
||||
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
|
||||
|
|
@ -563,11 +563,11 @@ declare_features! (
|
|||
/// Allows specifying nop padding on functions for dynamic patching.
|
||||
(unstable, patchable_function_entry, "1.81.0", Some(123115)),
|
||||
/// Experimental features that make `Pin` more ergonomic.
|
||||
(incomplete, pin_ergonomics, "CURRENT_RUSTC_VERSION", Some(130494)),
|
||||
(incomplete, pin_ergonomics, "1.83.0", Some(130494)),
|
||||
/// Allows postfix match `expr.match { ... }`
|
||||
(unstable, postfix_match, "1.79.0", Some(121618)),
|
||||
/// Allows `use<..>` precise capturign on impl Trait in traits.
|
||||
(unstable, precise_capturing_in_traits, "CURRENT_RUSTC_VERSION", Some(130044)),
|
||||
(unstable, precise_capturing_in_traits, "1.83.0", Some(130044)),
|
||||
/// Allows macro attributes on expressions, statements and non-inline modules.
|
||||
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
|
||||
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
use std::fmt;
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast::{
|
||||
Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitKind,
|
||||
TraitObjectSyntax, UintTy,
|
||||
self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
|
||||
LitKind, TraitObjectSyntax, UintTy,
|
||||
};
|
||||
pub use rustc_ast::{
|
||||
BinOp, BinOpKind, BindingMode, BorrowKind, ByRef, CaptureBy, ImplPolarity, IsAuto, Movability,
|
||||
|
|
@ -520,7 +519,7 @@ pub enum TraitBoundModifier {
|
|||
|
||||
#[derive(Clone, Copy, Debug, HashStable_Generic)]
|
||||
pub enum GenericBound<'hir> {
|
||||
Trait(PolyTraitRef<'hir>, TraitBoundModifier),
|
||||
Trait(PolyTraitRef<'hir>),
|
||||
Outlives(&'hir Lifetime),
|
||||
Use(&'hir [PreciseCapturingArg<'hir>], Span),
|
||||
}
|
||||
|
|
@ -528,7 +527,7 @@ pub enum GenericBound<'hir> {
|
|||
impl GenericBound<'_> {
|
||||
pub fn trait_ref(&self) -> Option<&TraitRef<'_>> {
|
||||
match self {
|
||||
GenericBound::Trait(data, _) => Some(&data.trait_ref),
|
||||
GenericBound::Trait(data) => Some(&data.trait_ref),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -2874,11 +2873,7 @@ pub enum TyKind<'hir> {
|
|||
OpaqueDef(&'hir OpaqueTy<'hir>, &'hir [GenericArg<'hir>]),
|
||||
/// A trait object type `Bound1 + Bound2 + Bound3`
|
||||
/// where `Bound` is a trait or a lifetime.
|
||||
TraitObject(
|
||||
&'hir [(PolyTraitRef<'hir>, TraitBoundModifier)],
|
||||
&'hir Lifetime,
|
||||
TraitObjectSyntax,
|
||||
),
|
||||
TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
|
||||
/// Unused for now.
|
||||
Typeof(&'hir AnonConst),
|
||||
/// `TyKind::Infer` means the type should be inferred instead of it having been
|
||||
|
|
@ -3182,6 +3177,11 @@ pub struct PolyTraitRef<'hir> {
|
|||
/// The `'a` in `for<'a> Foo<&'a T>`.
|
||||
pub bound_generic_params: &'hir [GenericParam<'hir>],
|
||||
|
||||
/// The constness and polarity of the trait ref.
|
||||
///
|
||||
/// The `async` modifier is lowered directly into a different trait for now.
|
||||
pub modifiers: TraitBoundModifier,
|
||||
|
||||
/// The `Foo<&'a T>` in `for<'a> Foo<&'a T>`.
|
||||
pub trait_ref: TraitRef<'hir>,
|
||||
|
||||
|
|
|
|||
|
|
@ -905,7 +905,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
|
|||
try_visit!(visitor.visit_array_length(length));
|
||||
}
|
||||
TyKind::TraitObject(bounds, ref lifetime, _syntax) => {
|
||||
for (bound, _modifier) in bounds {
|
||||
for bound in bounds {
|
||||
try_visit!(visitor.visit_poly_trait_ref(bound));
|
||||
}
|
||||
try_visit!(visitor.visit_lifetime(lifetime));
|
||||
|
|
@ -1160,7 +1160,7 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>(
|
|||
bound: &'v GenericBound<'v>,
|
||||
) -> V::Result {
|
||||
match *bound {
|
||||
GenericBound::Trait(ref typ, _modifier) => visitor.visit_poly_trait_ref(typ),
|
||||
GenericBound::Trait(ref typ) => visitor.visit_poly_trait_ref(typ),
|
||||
GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime),
|
||||
GenericBound::Use(args, _) => {
|
||||
walk_list!(visitor, visit_precise_capturing_arg, args);
|
||||
|
|
|
|||
|
|
@ -68,18 +68,21 @@ hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are fo
|
|||
hir_analysis_cmse_call_generic =
|
||||
function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
|
||||
|
||||
hir_analysis_cmse_call_inputs_stack_spill =
|
||||
arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
|
||||
hir_analysis_cmse_entry_generic =
|
||||
functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
|
||||
|
||||
hir_analysis_cmse_inputs_stack_spill =
|
||||
arguments for `"{$abi_name}"` function too large to pass via registers
|
||||
.label = {$plural ->
|
||||
[false] this argument doesn't
|
||||
*[true] these arguments don't
|
||||
} fit in the available registers
|
||||
.note = functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
|
||||
.note = functions with the `"{$abi_name}"` ABI must pass all their arguments via the 4 32-bit available argument registers
|
||||
|
||||
hir_analysis_cmse_call_output_stack_spill =
|
||||
return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
|
||||
hir_analysis_cmse_output_stack_spill =
|
||||
return value of `"{$abi_name}"` function too large to pass via registers
|
||||
.label = this type doesn't fit in the available registers
|
||||
.note1 = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
|
||||
.note1 = functions with the `"{$abi_name}"` ABI must pass their result via the available return registers
|
||||
.note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
|
||||
|
||||
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_infer::traits::PredicateObligations;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_session::Limit;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -23,7 +24,7 @@ struct AutoderefSnapshot<'tcx> {
|
|||
reached_recursion_limit: bool,
|
||||
steps: Vec<(Ty<'tcx>, AutoderefKind)>,
|
||||
cur_ty: Ty<'tcx>,
|
||||
obligations: Vec<traits::PredicateObligation<'tcx>>,
|
||||
obligations: PredicateObligations<'tcx>,
|
||||
}
|
||||
|
||||
pub struct Autoderef<'a, 'tcx> {
|
||||
|
|
@ -119,7 +120,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
|||
state: AutoderefSnapshot {
|
||||
steps: vec![],
|
||||
cur_ty: infcx.resolve_vars_if_possible(base_ty),
|
||||
obligations: vec![],
|
||||
obligations: PredicateObligations::new(),
|
||||
at_start: true,
|
||||
reached_recursion_limit: false,
|
||||
},
|
||||
|
|
@ -165,7 +166,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
|||
pub fn structurally_normalize(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> {
|
||||
) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> {
|
||||
let ocx = ObligationCtxt::new(self.infcx);
|
||||
let Ok(normalized_ty) = ocx.structurally_normalize(
|
||||
&traits::ObligationCause::misc(self.span, self.body_id),
|
||||
|
|
@ -204,11 +205,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
|||
self.state.steps.len()
|
||||
}
|
||||
|
||||
pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
|
||||
pub fn into_obligations(self) -> PredicateObligations<'tcx> {
|
||||
self.state.obligations
|
||||
}
|
||||
|
||||
pub fn current_obligations(&self) -> Vec<traits::PredicateObligation<'tcx>> {
|
||||
pub fn current_obligations(&self) -> PredicateObligations<'tcx> {
|
||||
self.state.obligations.clone()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,9 +61,9 @@ pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ab
|
|||
Some(true) => (),
|
||||
Some(false) | None => {
|
||||
tcx.node_span_lint(UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, hir_id, span, |lint| {
|
||||
lint.primary_message(
|
||||
"use of calling convention not supported on this target on function pointer",
|
||||
);
|
||||
lint.primary_message(format!(
|
||||
"the calling convention {abi} is not supported on this target"
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -592,14 +592,13 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
|||
&cause,
|
||||
hir.get_if_local(impl_m.def_id)
|
||||
.and_then(|node| node.fn_decl())
|
||||
.map(|decl| (decl.output.span(), Cow::from("return type in trait"))),
|
||||
.map(|decl| (decl.output.span(), Cow::from("return type in trait"), false)),
|
||||
Some(infer::ValuePairs::Terms(ExpectedFound {
|
||||
expected: trait_return_ty.into(),
|
||||
found: impl_return_ty.into(),
|
||||
})),
|
||||
terr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
return Err(diag.emit());
|
||||
}
|
||||
|
|
@ -1018,14 +1017,13 @@ fn report_trait_method_mismatch<'tcx>(
|
|||
infcx.err_ctxt().note_type_err(
|
||||
&mut diag,
|
||||
&cause,
|
||||
trait_err_span.map(|sp| (sp, Cow::from("type in trait"))),
|
||||
trait_err_span.map(|sp| (sp, Cow::from("type in trait"), false)),
|
||||
Some(infer::ValuePairs::PolySigs(ExpectedFound {
|
||||
expected: ty::Binder::dummy(trait_sig),
|
||||
found: ty::Binder::dummy(impl_sig),
|
||||
})),
|
||||
terr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
diag.emit()
|
||||
|
|
@ -1825,14 +1823,13 @@ fn compare_const_predicate_entailment<'tcx>(
|
|||
infcx.err_ctxt().note_type_err(
|
||||
&mut diag,
|
||||
&cause,
|
||||
trait_c_span.map(|span| (span, Cow::from("type in trait"))),
|
||||
trait_c_span.map(|span| (span, Cow::from("type in trait"), false)),
|
||||
Some(infer::ValuePairs::Terms(ExpectedFound {
|
||||
expected: trait_ty.into(),
|
||||
found: impl_ty.into(),
|
||||
})),
|
||||
terr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
return Err(diag.emit());
|
||||
};
|
||||
|
|
|
|||
|
|
@ -652,7 +652,6 @@ pub fn check_function_signature<'tcx>(
|
|||
})),
|
||||
err,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
return Err(diag.emit());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -531,7 +531,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) {
|
|||
debug!(?required_bounds);
|
||||
let param_env = tcx.param_env(gat_def_id);
|
||||
|
||||
let mut unsatisfied_bounds: Vec<_> = required_bounds
|
||||
let unsatisfied_bounds: Vec<_> = required_bounds
|
||||
.into_iter()
|
||||
.filter(|clause| match clause.kind().skip_binder() {
|
||||
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => {
|
||||
|
|
@ -552,9 +552,6 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) {
|
|||
.map(|clause| clause.to_string())
|
||||
.collect();
|
||||
|
||||
// We sort so that order is predictable
|
||||
unsatisfied_bounds.sort();
|
||||
|
||||
if !unsatisfied_bounds.is_empty() {
|
||||
let plural = pluralize!(unsatisfied_bounds.len());
|
||||
let suggestion = format!(
|
||||
|
|
@ -832,7 +829,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {
|
|||
|
||||
fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
|
||||
match ty.kind {
|
||||
hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments {
|
||||
hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
|
||||
[s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
|
||||
_ => false,
|
||||
},
|
||||
|
|
@ -1878,24 +1875,15 @@ fn check_variances_for_type_defn<'tcx>(
|
|||
item: &'tcx hir::Item<'tcx>,
|
||||
hir_generics: &hir::Generics<'tcx>,
|
||||
) {
|
||||
let identity_args = ty::GenericArgs::identity_for_item(tcx, item.owner_id);
|
||||
|
||||
match item.kind {
|
||||
ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
|
||||
for field in tcx.adt_def(item.owner_id).all_fields() {
|
||||
if field.ty(tcx, identity_args).references_error() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Ok
|
||||
}
|
||||
ItemKind::TyAlias(..) => {
|
||||
assert!(
|
||||
tcx.type_alias_is_lazy(item.owner_id),
|
||||
"should not be computing variance of non-weak type alias"
|
||||
);
|
||||
if tcx.type_of(item.owner_id).skip_binder().references_error() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
kind => span_bug!(item.span, "cannot compute the variances of {kind:?}"),
|
||||
}
|
||||
|
|
@ -1958,6 +1946,15 @@ fn check_variances_for_type_defn<'tcx>(
|
|||
continue;
|
||||
}
|
||||
|
||||
// Look for `ErrorGuaranteed` deeply within this type.
|
||||
if let ControlFlow::Break(ErrorGuaranteed { .. }) = tcx
|
||||
.type_of(item.owner_id)
|
||||
.instantiate_identity()
|
||||
.visit_with(&mut HasErrorDeep { tcx, seen: Default::default() })
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
match hir_param.name {
|
||||
hir::ParamName::Error => {}
|
||||
_ => {
|
||||
|
|
@ -1968,6 +1965,46 @@ fn check_variances_for_type_defn<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Look for `ErrorGuaranteed` deeply within structs' (unsubstituted) fields.
|
||||
struct HasErrorDeep<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
seen: FxHashSet<DefId>,
|
||||
}
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasErrorDeep<'tcx> {
|
||||
type Result = ControlFlow<ErrorGuaranteed>;
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
match *ty.kind() {
|
||||
ty::Adt(def, _) => {
|
||||
if self.seen.insert(def.did()) {
|
||||
for field in def.all_fields() {
|
||||
self.tcx.type_of(field.did).instantiate_identity().visit_with(self)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::Error(guar) => return ControlFlow::Break(guar),
|
||||
_ => {}
|
||||
}
|
||||
ty.super_visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
|
||||
if let Err(guar) = r.error_reported() {
|
||||
ControlFlow::Break(guar)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result {
|
||||
if let Err(guar) = c.error_reported() {
|
||||
ControlFlow::Break(guar)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn report_bivariance<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param: &'tcx hir::GenericParam<'tcx>,
|
||||
|
|
|
|||
|
|
@ -319,9 +319,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
|
|||
// List of connected regions is built. Now, run the overlap check
|
||||
// for each pair of impl blocks in the same connected region.
|
||||
for region in connected_regions.into_iter().flatten() {
|
||||
let mut impl_blocks =
|
||||
region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
|
||||
impl_blocks.sort_unstable();
|
||||
let impl_blocks = region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
|
||||
for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
|
||||
let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
|
||||
res = res.and(self.check_for_duplicate_items_in_impl(impl1_def_id));
|
||||
|
|
|
|||
|
|
@ -866,7 +866,7 @@ impl<'tcx> ItemCtxt<'tcx> {
|
|||
#[instrument(level = "trace", skip(self))]
|
||||
fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool {
|
||||
match b {
|
||||
hir::GenericBound::Trait(poly_trait_ref, _) => {
|
||||
hir::GenericBound::Trait(poly_trait_ref) => {
|
||||
let trait_ref = &poly_trait_ref.trait_ref;
|
||||
if let Some(trait_did) = trait_ref.trait_def_id() {
|
||||
self.tcx.trait_may_define_assoc_item(trait_did, assoc_name)
|
||||
|
|
|
|||
|
|
@ -644,7 +644,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
|||
debug!(?bounds, ?lifetime, "TraitObject");
|
||||
let scope = Scope::TraitRefBoundary { s: self.scope };
|
||||
self.with(scope, |this| {
|
||||
for (bound, _) in bounds {
|
||||
for bound in bounds {
|
||||
this.visit_poly_trait_ref_inner(
|
||||
bound,
|
||||
NonLifetimeBinderAllowed::Deny("trait object types"),
|
||||
|
|
@ -1918,7 +1918,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
predicate.bounds.iter().find_map(|bound| {
|
||||
let hir::GenericBound::Trait(trait_, _) = bound else {
|
||||
let hir::GenericBound::Trait(trait_) = bound else {
|
||||
return None;
|
||||
};
|
||||
BoundVarContext::supertrait_hrtb_vars(
|
||||
|
|
|
|||
|
|
@ -1627,23 +1627,25 @@ pub(crate) struct InvalidReceiverTy<'tcx> {
|
|||
pub(crate) struct EffectsWithoutNextSolver;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_cmse_call_inputs_stack_spill, code = E0798)]
|
||||
#[diag(hir_analysis_cmse_inputs_stack_spill, code = E0798)]
|
||||
#[note]
|
||||
pub(crate) struct CmseCallInputsStackSpill {
|
||||
pub(crate) struct CmseInputsStackSpill {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub plural: bool,
|
||||
pub abi_name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)]
|
||||
#[diag(hir_analysis_cmse_output_stack_spill, code = E0798)]
|
||||
#[note(hir_analysis_note1)]
|
||||
#[note(hir_analysis_note2)]
|
||||
pub(crate) struct CmseCallOutputStackSpill {
|
||||
pub(crate) struct CmseOutputStackSpill {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub abi_name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -1659,3 +1661,10 @@ pub(crate) struct BadReturnTypeNotation {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_cmse_entry_generic, code = E0798)]
|
||||
pub(crate) struct CmseEntryGeneric {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,10 +44,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let mut unbounds: SmallVec<[_; 1]> = SmallVec::new();
|
||||
let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| {
|
||||
for hir_bound in hir_bounds {
|
||||
let hir::GenericBound::Trait(ptr, modifier) = hir_bound else {
|
||||
let hir::GenericBound::Trait(ptr) = hir_bound else {
|
||||
continue;
|
||||
};
|
||||
match modifier {
|
||||
match ptr.modifiers {
|
||||
hir::TraitBoundModifier::Maybe => unbounds.push(ptr),
|
||||
hir::TraitBoundModifier::Negative => {
|
||||
if let Some(sized_def_id) = sized_def_id
|
||||
|
|
@ -156,8 +156,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
{
|
||||
for hir_bound in hir_bounds {
|
||||
match hir_bound {
|
||||
hir::GenericBound::Trait(poly_trait_ref, modifier) => {
|
||||
let (constness, polarity) = match modifier {
|
||||
hir::GenericBound::Trait(poly_trait_ref) => {
|
||||
let (constness, polarity) = match poly_trait_ref.modifiers {
|
||||
hir::TraitBoundModifier::Const => {
|
||||
(ty::BoundConstness::Const, ty::PredicatePolarity::Positive)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err};
|
||||
use rustc_hir::{self as hir, HirId};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::layout::LayoutError;
|
||||
use rustc_middle::ty::{self, ParamEnv, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::abi;
|
||||
|
||||
use crate::errors;
|
||||
|
|
@ -17,61 +17,104 @@ pub(crate) fn validate_cmse_abi<'tcx>(
|
|||
abi: abi::Abi,
|
||||
fn_sig: ty::PolyFnSig<'tcx>,
|
||||
) {
|
||||
if let abi::Abi::CCmseNonSecureCall = abi {
|
||||
let hir_node = tcx.hir_node(hir_id);
|
||||
let hir::Node::Ty(hir::Ty {
|
||||
span: bare_fn_span,
|
||||
kind: hir::TyKind::BareFn(bare_fn_ty),
|
||||
..
|
||||
}) = hir_node
|
||||
else {
|
||||
let span = match tcx.parent_hir_node(hir_id) {
|
||||
hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::ForeignMod { .. }, span, ..
|
||||
}) => *span,
|
||||
_ => tcx.hir().span(hir_id),
|
||||
let abi_name = abi.name();
|
||||
|
||||
match abi {
|
||||
abi::Abi::CCmseNonSecureCall => {
|
||||
let hir_node = tcx.hir_node(hir_id);
|
||||
let hir::Node::Ty(hir::Ty {
|
||||
span: bare_fn_span,
|
||||
kind: hir::TyKind::BareFn(bare_fn_ty),
|
||||
..
|
||||
}) = hir_node
|
||||
else {
|
||||
let span = match tcx.parent_hir_node(hir_id) {
|
||||
hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::ForeignMod { .. },
|
||||
span,
|
||||
..
|
||||
}) => *span,
|
||||
_ => tcx.hir().span(hir_id),
|
||||
};
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
span,
|
||||
E0781,
|
||||
"the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
|
||||
)
|
||||
.emit();
|
||||
return;
|
||||
};
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
span,
|
||||
E0781,
|
||||
"the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
|
||||
)
|
||||
.emit();
|
||||
return;
|
||||
};
|
||||
|
||||
match is_valid_cmse_inputs(tcx, fn_sig) {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(index)) => {
|
||||
// fn(x: u32, u32, u32, u16, y: u16) -> u32,
|
||||
// ^^^^^^
|
||||
let span = bare_fn_ty.param_names[index]
|
||||
.span
|
||||
.to(bare_fn_ty.decl.inputs[index].span)
|
||||
.to(bare_fn_ty.decl.inputs.last().unwrap().span);
|
||||
let plural = bare_fn_ty.param_names.len() - index != 1;
|
||||
dcx.emit_err(errors::CmseCallInputsStackSpill { span, plural });
|
||||
}
|
||||
Err(layout_err) => {
|
||||
if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
|
||||
dcx.emit_err(err);
|
||||
match is_valid_cmse_inputs(tcx, fn_sig) {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(index)) => {
|
||||
// fn(x: u32, u32, u32, u16, y: u16) -> u32,
|
||||
// ^^^^^^
|
||||
let span = bare_fn_ty.param_names[index]
|
||||
.span
|
||||
.to(bare_fn_ty.decl.inputs[index].span)
|
||||
.to(bare_fn_ty.decl.inputs.last().unwrap().span);
|
||||
let plural = bare_fn_ty.param_names.len() - index != 1;
|
||||
dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi_name });
|
||||
}
|
||||
Err(layout_err) => {
|
||||
if should_emit_generic_error(abi, layout_err) {
|
||||
dcx.emit_err(errors::CmseCallGeneric { span: *bare_fn_span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match is_valid_cmse_output(tcx, fn_sig) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
let span = bare_fn_ty.decl.output.span();
|
||||
dcx.emit_err(errors::CmseOutputStackSpill { span, abi_name });
|
||||
}
|
||||
Err(layout_err) => {
|
||||
if should_emit_generic_error(abi, layout_err) {
|
||||
dcx.emit_err(errors::CmseCallGeneric { span: *bare_fn_span });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
abi::Abi::CCmseNonSecureEntry => {
|
||||
let hir_node = tcx.hir_node(hir_id);
|
||||
let Some(hir::FnSig { decl, span: fn_sig_span, .. }) = hir_node.fn_sig() else {
|
||||
// might happen when this ABI is used incorrectly. That will be handled elsewhere
|
||||
return;
|
||||
};
|
||||
|
||||
match is_valid_cmse_output(tcx, fn_sig) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
let span = bare_fn_ty.decl.output.span();
|
||||
dcx.emit_err(errors::CmseCallOutputStackSpill { span });
|
||||
}
|
||||
Err(layout_err) => {
|
||||
if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
|
||||
dcx.emit_err(err);
|
||||
match is_valid_cmse_inputs(tcx, fn_sig) {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(index)) => {
|
||||
// fn f(x: u32, y: u32, z: u32, w: u16, q: u16) -> u32,
|
||||
// ^^^^^^
|
||||
let span = decl.inputs[index].span.to(decl.inputs.last().unwrap().span);
|
||||
let plural = decl.inputs.len() - index != 1;
|
||||
dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi_name });
|
||||
}
|
||||
Err(layout_err) => {
|
||||
if should_emit_generic_error(abi, layout_err) {
|
||||
dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match is_valid_cmse_output(tcx, fn_sig) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
let span = decl.output.span();
|
||||
dcx.emit_err(errors::CmseOutputStackSpill { span, abi_name });
|
||||
}
|
||||
Err(layout_err) => {
|
||||
if should_emit_generic_error(abi, layout_err) {
|
||||
dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,22 +195,22 @@ fn is_valid_cmse_output<'tcx>(
|
|||
Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64)
|
||||
}
|
||||
|
||||
fn cmse_layout_err<'tcx>(
|
||||
layout_err: &'tcx LayoutError<'tcx>,
|
||||
span: Span,
|
||||
) -> Option<crate::errors::CmseCallGeneric> {
|
||||
fn should_emit_generic_error<'tcx>(abi: abi::Abi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
|
||||
use LayoutError::*;
|
||||
|
||||
match layout_err {
|
||||
Unknown(ty) => {
|
||||
if ty.is_impl_trait() {
|
||||
None // prevent double reporting of this error
|
||||
} else {
|
||||
Some(errors::CmseCallGeneric { span })
|
||||
match abi {
|
||||
abi::Abi::CCmseNonSecureCall => {
|
||||
// prevent double reporting of this error
|
||||
!ty.is_impl_trait()
|
||||
}
|
||||
abi::Abi::CCmseNonSecureEntry => true,
|
||||
_ => bug!("invalid ABI: {abi}"),
|
||||
}
|
||||
}
|
||||
SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => {
|
||||
None // not our job to report these
|
||||
false // not our job to report these
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
&self,
|
||||
span: Span,
|
||||
hir_id: hir::HirId,
|
||||
hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)],
|
||||
hir_trait_bounds: &[hir::PolyTraitRef<'tcx>],
|
||||
lifetime: &hir::Lifetime,
|
||||
representation: DynKind,
|
||||
) -> Ty<'tcx> {
|
||||
|
|
@ -39,8 +39,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let mut bounds = Bounds::default();
|
||||
let mut potential_assoc_types = Vec::new();
|
||||
let dummy_self = self.tcx().types.trait_object_dummy_self;
|
||||
for (trait_bound, modifier) in hir_trait_bounds.iter().rev() {
|
||||
if *modifier == hir::TraitBoundModifier::Maybe {
|
||||
for trait_bound in hir_trait_bounds.iter().rev() {
|
||||
// FIXME: This doesn't handle `? const`.
|
||||
if trait_bound.modifiers == hir::TraitBoundModifier::Maybe {
|
||||
continue;
|
||||
}
|
||||
if let GenericArgCountResult {
|
||||
|
|
@ -263,7 +264,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let args = tcx.mk_args(&args);
|
||||
|
||||
let span = i.bottom().1;
|
||||
let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| {
|
||||
let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
|
||||
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
|
||||
&& hir_bound.span.contains(span)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -718,7 +718,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
&self,
|
||||
associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
|
||||
potential_assoc_types: Vec<usize>,
|
||||
trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)],
|
||||
trait_bounds: &[hir::PolyTraitRef<'_>],
|
||||
) {
|
||||
if associated_types.values().all(|v| v.is_empty()) {
|
||||
return;
|
||||
|
|
@ -764,12 +764,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// related to issue #91997, turbofishes added only when in an expr or pat
|
||||
let mut in_expr_or_pat = false;
|
||||
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
|
||||
let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.0.trait_ref.hir_ref_id));
|
||||
let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
|
||||
in_expr_or_pat = match grandparent {
|
||||
hir::Node::Expr(_) | hir::Node::Pat(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
match bound.0.trait_ref.path.segments {
|
||||
match bound.trait_ref.path.segments {
|
||||
// FIXME: `trait_ref.path.span` can point to a full path with multiple
|
||||
// segments, even though `trait_ref.path.segments` is of length `1`. Work
|
||||
// around that bug here, even though it should be fixed elsewhere.
|
||||
|
|
@ -810,7 +810,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// and we can then use their span to indicate this to the user.
|
||||
let bound_names = trait_bounds
|
||||
.iter()
|
||||
.filter_map(|(poly_trait_ref, _)| {
|
||||
.filter_map(|poly_trait_ref| {
|
||||
let path = poly_trait_ref.trait_ref.path.segments.last()?;
|
||||
let args = path.args?;
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
.ok()
|
||||
.is_some_and(|s| s.trim_end().ends_with('<'));
|
||||
|
||||
let is_global = poly_trait_ref.0.trait_ref.path.is_global();
|
||||
let is_global = poly_trait_ref.trait_ref.path.is_global();
|
||||
|
||||
let mut sugg = vec![(
|
||||
self_ty.span.shrink_to_lo(),
|
||||
|
|
@ -211,7 +211,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// Check if trait object is safe for suggesting dynamic dispatch.
|
||||
let is_dyn_compatible = match self_ty.kind {
|
||||
hir::TyKind::TraitObject(objects, ..) => {
|
||||
objects.iter().all(|(o, _)| match o.trait_ref.path.res {
|
||||
objects.iter().all(|o| match o.trait_ref.path.res {
|
||||
Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id),
|
||||
_ => false,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -301,16 +301,13 @@ impl<'a> State<'a> {
|
|||
self.word_space("dyn");
|
||||
}
|
||||
let mut first = true;
|
||||
for (bound, modifier) in bounds {
|
||||
for bound in bounds {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
self.nbsp();
|
||||
self.word_space("+");
|
||||
}
|
||||
if *modifier == TraitBoundModifier::Maybe {
|
||||
self.word("?");
|
||||
}
|
||||
self.print_poly_trait_ref(bound);
|
||||
}
|
||||
if !lifetime.is_elided() {
|
||||
|
|
@ -679,6 +676,10 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
fn print_poly_trait_ref(&mut self, t: &hir::PolyTraitRef<'_>) {
|
||||
// FIXME: This isn't correct!
|
||||
if t.modifiers == TraitBoundModifier::Maybe {
|
||||
self.word("?");
|
||||
}
|
||||
self.print_formal_generic_params(t.bound_generic_params);
|
||||
self.print_trait_ref(&t.trait_ref);
|
||||
}
|
||||
|
|
@ -2077,10 +2078,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
match bound {
|
||||
GenericBound::Trait(tref, modifier) => {
|
||||
if modifier == &TraitBoundModifier::Maybe {
|
||||
self.word("?");
|
||||
}
|
||||
GenericBound::Trait(tref) => {
|
||||
self.print_poly_trait_ref(tref);
|
||||
}
|
||||
GenericBound::Outlives(lt) => {
|
||||
|
|
|
|||
|
|
@ -235,8 +235,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Some(ret_coercion) => {
|
||||
let ret_ty = ret_coercion.borrow().expected_ty();
|
||||
let ret_ty = self.infcx.shallow_resolve(ret_ty);
|
||||
self.can_coerce(arm_ty, ret_ty)
|
||||
&& prior_arm.is_none_or(|(_, ty, _)| self.can_coerce(ty, ret_ty))
|
||||
self.may_coerce(arm_ty, ret_ty)
|
||||
&& prior_arm.is_none_or(|(_, ty, _)| self.may_coerce(ty, ret_ty))
|
||||
// The match arms need to unify for the case of `impl Trait`.
|
||||
&& !matches!(ret_ty.kind(), ty::Alias(ty::Opaque, ..))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::iter;
|
|||
use itertools::Itertools;
|
||||
use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind};
|
||||
use rustc_infer::infer::InferOk;
|
||||
use rustc_infer::traits::PredicateObligations;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
|
|
@ -36,10 +37,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
|
||||
let steps = autoderef.steps();
|
||||
if steps.is_empty() {
|
||||
return InferOk { obligations: vec![], value: vec![] };
|
||||
return InferOk { obligations: PredicateObligations::new(), value: vec![] };
|
||||
}
|
||||
|
||||
let mut obligations = vec![];
|
||||
let mut obligations = PredicateObligations::new();
|
||||
let targets =
|
||||
steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false)));
|
||||
let steps: Vec<_> = steps
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
let mut sugg_mutref = false;
|
||||
if let ty::Ref(reg, cast_ty, mutbl) = *self.cast_ty.kind() {
|
||||
if let ty::RawPtr(expr_ty, _) = *self.expr_ty.kind()
|
||||
&& fcx.can_coerce(
|
||||
&& fcx.may_coerce(
|
||||
Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, expr_ty, mutbl),
|
||||
self.cast_ty,
|
||||
)
|
||||
|
|
@ -418,14 +418,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
} else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind()
|
||||
&& expr_mutbl == Mutability::Not
|
||||
&& mutbl == Mutability::Mut
|
||||
&& fcx.can_coerce(Ty::new_mut_ref(fcx.tcx, expr_reg, expr_ty), self.cast_ty)
|
||||
&& fcx.may_coerce(Ty::new_mut_ref(fcx.tcx, expr_reg, expr_ty), self.cast_ty)
|
||||
{
|
||||
sugg_mutref = true;
|
||||
}
|
||||
|
||||
if !sugg_mutref
|
||||
&& sugg == None
|
||||
&& fcx.can_coerce(
|
||||
&& fcx.may_coerce(
|
||||
Ty::new_ref(fcx.tcx, reg, self.expr_ty, mutbl),
|
||||
self.cast_ty,
|
||||
)
|
||||
|
|
@ -433,7 +433,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
sugg = Some((format!("&{}", mutbl.prefix_str()), false));
|
||||
}
|
||||
} else if let ty::RawPtr(_, mutbl) = *self.cast_ty.kind()
|
||||
&& fcx.can_coerce(
|
||||
&& fcx.may_coerce(
|
||||
Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, self.expr_ty, mutbl),
|
||||
self.cast_ty,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult};
|
||||
use rustc_infer::traits::ObligationCauseCode;
|
||||
use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
|
||||
use rustc_macros::{TypeFoldable, TypeVisitable};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
|
||||
|
|
@ -805,7 +805,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706
|
||||
// [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796
|
||||
self.commit_if_ok(|_| {
|
||||
let mut all_obligations = vec![];
|
||||
let mut all_obligations = PredicateObligations::new();
|
||||
let supplied_sig = self.instantiate_binder_with_fresh_vars(
|
||||
self.tcx.def_span(expr_def_id),
|
||||
BoundRegionConversionTime::FnCall,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ use rustc_infer::infer::relate::RelateResult;
|
|||
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
|
||||
use rustc_infer::traits::{
|
||||
IfExpressionCause, MatchExpressionArmCause, Obligation, PredicateObligation,
|
||||
PredicateObligations,
|
||||
};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::span_bug;
|
||||
|
|
@ -120,7 +121,7 @@ fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'
|
|||
fn success<'tcx>(
|
||||
adj: Vec<Adjustment<'tcx>>,
|
||||
target: Ty<'tcx>,
|
||||
obligations: Vec<traits::PredicateObligation<'tcx>>,
|
||||
obligations: PredicateObligations<'tcx>,
|
||||
) -> CoerceResult<'tcx> {
|
||||
Ok(InferOk { value: (adj, target), obligations })
|
||||
}
|
||||
|
|
@ -184,7 +185,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
// Coercing from `!` to any type is allowed:
|
||||
if a.is_never() {
|
||||
if self.coerce_never {
|
||||
return success(simple(Adjust::NeverToAny)(b), b, vec![]);
|
||||
return success(simple(Adjust::NeverToAny)(b), b, PredicateObligations::new());
|
||||
} else {
|
||||
// Otherwise the only coercion we can do is unification.
|
||||
return self.unify_and(a, b, identity);
|
||||
|
|
@ -278,7 +279,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
// Two unresolved type variables: create a `Coerce` predicate.
|
||||
let target_ty = if self.use_lub { self.next_ty_var(self.cause.span) } else { b };
|
||||
|
||||
let mut obligations = Vec::with_capacity(2);
|
||||
let mut obligations = PredicateObligations::with_capacity(2);
|
||||
for &source_ty in &[a, b] {
|
||||
if source_ty != target_ty {
|
||||
obligations.push(Obligation::new(
|
||||
|
|
@ -744,7 +745,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
|
||||
// Check the obligations of the cast -- for example, when casting
|
||||
// `usize` to `dyn* Clone + 'static`:
|
||||
let mut obligations: Vec<_> = predicates
|
||||
let obligations = predicates
|
||||
.iter()
|
||||
.map(|predicate| {
|
||||
// For each existential predicate (e.g., `?Self: Clone`) instantiate
|
||||
|
|
@ -764,21 +765,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
ty::OutlivesPredicate(a, b_region),
|
||||
))),
|
||||
),
|
||||
// Enforce that the type is `usize`/pointer-sized.
|
||||
Obligation::new(
|
||||
self.tcx,
|
||||
self.cause.clone(),
|
||||
self.param_env,
|
||||
ty::TraitRef::new(
|
||||
self.tcx,
|
||||
self.tcx
|
||||
.require_lang_item(hir::LangItem::PointerLike, Some(self.cause.span)),
|
||||
[a],
|
||||
),
|
||||
),
|
||||
])
|
||||
.collect();
|
||||
|
||||
// Enforce that the type is `usize`/pointer-sized.
|
||||
obligations.push(Obligation::new(
|
||||
self.tcx,
|
||||
self.cause.clone(),
|
||||
self.param_env,
|
||||
ty::TraitRef::new(
|
||||
self.tcx,
|
||||
self.tcx.require_lang_item(hir::LangItem::PointerLike, Some(self.cause.span)),
|
||||
[a],
|
||||
),
|
||||
));
|
||||
|
||||
Ok(InferOk {
|
||||
value: (
|
||||
vec![Adjustment { kind: Adjust::Pointer(PointerCoercion::DynStar), target: b }],
|
||||
|
|
@ -1083,24 +1084,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Same as `coerce()`, but without side-effects.
|
||||
/// Probe whether `expr_ty` can be coerced to `target_ty`. This has no side-effects,
|
||||
/// and may return false positives if types are not yet fully constrained by inference.
|
||||
///
|
||||
/// Returns false if the coercion creates any obligations that result in
|
||||
/// errors.
|
||||
pub(crate) fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool {
|
||||
// FIXME(-Znext-solver): We need to structurally resolve both types here.
|
||||
let source = self.resolve_vars_with_obligations(expr_ty);
|
||||
debug!("coercion::can_with_predicates({:?} -> {:?})", source, target);
|
||||
|
||||
/// Returns false if the coercion is not possible, or if the coercion creates any
|
||||
/// sub-obligations that result in errors.
|
||||
///
|
||||
/// This should only be used for diagnostics.
|
||||
pub(crate) fn may_coerce(&self, expr_ty: Ty<'tcx>, target_ty: Ty<'tcx>) -> bool {
|
||||
let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable);
|
||||
// We don't ever need two-phase here since we throw out the result of the coercion.
|
||||
// We also just always set `coerce_never` to true, since this is a heuristic.
|
||||
let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
|
||||
let coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No, true);
|
||||
self.probe(|_| {
|
||||
let Ok(ok) = coerce.coerce(source, target) else {
|
||||
// Make sure to structurally resolve the types, since we use
|
||||
// the `TyKind`s heavily in coercion.
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
let structurally_resolve = |ty| {
|
||||
let ty = self.shallow_resolve(ty);
|
||||
if self.next_trait_solver()
|
||||
&& let ty::Alias(..) = ty.kind()
|
||||
{
|
||||
ocx.structurally_normalize(&cause, self.param_env, ty)
|
||||
} else {
|
||||
Ok(ty)
|
||||
}
|
||||
};
|
||||
let Ok(expr_ty) = structurally_resolve(expr_ty) else {
|
||||
return false;
|
||||
};
|
||||
let Ok(target_ty) = structurally_resolve(target_ty) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Ok(ok) = coerce.coerce(expr_ty, target_ty) else {
|
||||
return false;
|
||||
};
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
ocx.register_obligations(ok.obligations);
|
||||
ocx.select_where_possible().is_empty()
|
||||
})
|
||||
|
|
@ -1369,7 +1388,7 @@ pub fn can_coerce<'tcx>(
|
|||
) -> bool {
|
||||
let root_ctxt = crate::typeck_root_ctxt::TypeckRootCtxt::new(tcx, body_id);
|
||||
let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, body_id);
|
||||
fn_ctxt.can_coerce(ty, output_ty)
|
||||
fn_ctxt.may_coerce(ty, output_ty)
|
||||
}
|
||||
|
||||
/// CoerceMany encapsulates the pattern you should use when you have
|
||||
|
|
|
|||
|
|
@ -1330,9 +1330,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| {
|
||||
let lhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, lhs.peel_refs());
|
||||
let rhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rhs.peel_refs());
|
||||
self.can_coerce(rhs, lhs)
|
||||
self.may_coerce(rhs, lhs)
|
||||
};
|
||||
let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) {
|
||||
let (applicability, eq) = if self.may_coerce(rhs_ty, lhs_ty) {
|
||||
(Applicability::MachineApplicable, true)
|
||||
} else if refs_can_coerce(rhs_ty, lhs_ty) {
|
||||
// The lhs and rhs are likely missing some references in either side. Subsequent
|
||||
|
|
@ -1349,7 +1349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let actual_lhs_ty = self.check_expr(rhs_expr);
|
||||
(
|
||||
Applicability::MaybeIncorrect,
|
||||
self.can_coerce(rhs_ty, actual_lhs_ty)
|
||||
self.may_coerce(rhs_ty, actual_lhs_ty)
|
||||
|| refs_can_coerce(rhs_ty, actual_lhs_ty),
|
||||
)
|
||||
} else if let ExprKind::Binary(
|
||||
|
|
@ -1363,7 +1363,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let actual_rhs_ty = self.check_expr(lhs_expr);
|
||||
(
|
||||
Applicability::MaybeIncorrect,
|
||||
self.can_coerce(actual_rhs_ty, lhs_ty)
|
||||
self.may_coerce(actual_rhs_ty, lhs_ty)
|
||||
|| refs_can_coerce(actual_rhs_ty, lhs_ty),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -1414,7 +1414,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.param_env,
|
||||
)
|
||||
.may_apply();
|
||||
if lhs_deref_ty_is_sized && self.can_coerce(rhs_ty, lhs_deref_ty) {
|
||||
if lhs_deref_ty_is_sized && self.may_coerce(rhs_ty, lhs_deref_ty) {
|
||||
err.span_suggestion_verbose(
|
||||
lhs.span.shrink_to_lo(),
|
||||
"consider dereferencing here to assign to the mutably borrowed value",
|
||||
|
|
|
|||
|
|
@ -658,7 +658,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
&& fn_sig.inputs()[1..]
|
||||
.iter()
|
||||
.zip(input_types.iter())
|
||||
.all(|(expected, found)| self.can_coerce(*expected, *found))
|
||||
.all(|(expected, found)| self.may_coerce(*expected, *found))
|
||||
&& fn_sig.inputs()[1..].len() == input_types.len()
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
|
|
@ -722,7 +722,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
let expectation = Expectation::rvalue_hint(self, expected_input_ty);
|
||||
let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
|
||||
let can_coerce = self.can_coerce(arg_ty, coerced_ty);
|
||||
let can_coerce = self.may_coerce(arg_ty, coerced_ty);
|
||||
if !can_coerce {
|
||||
return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts(
|
||||
ty::error::ExpectedFound::new(true, coerced_ty, arg_ty),
|
||||
|
|
@ -802,7 +802,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()),
|
||||
),
|
||||
) {
|
||||
if !self.can_coerce(provided_ty, *expected_ty) {
|
||||
if !self.may_coerce(provided_ty, *expected_ty) {
|
||||
satisfied = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1023,7 +1023,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
std::iter::zip(formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all(
|
||||
|((expected_ty, _), (provided_ty, _))| {
|
||||
!provided_ty.references_error()
|
||||
&& self.can_coerce(*provided_ty, *expected_ty)
|
||||
&& self.may_coerce(*provided_ty, *expected_ty)
|
||||
},
|
||||
)
|
||||
};
|
||||
|
|
@ -1114,7 +1114,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
None,
|
||||
Some(trace.values),
|
||||
e,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
|
@ -2124,7 +2123,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let expr_ty = self.typeck_results.borrow().expr_ty(expr);
|
||||
let return_ty = fn_sig.output();
|
||||
if !matches!(expr.kind, hir::ExprKind::Ret(..))
|
||||
&& self.can_coerce(expr_ty, return_ty)
|
||||
&& self.may_coerce(expr_ty, return_ty)
|
||||
{
|
||||
found_semi = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! A utility module to inspect currently ambiguous obligations in the current context.
|
||||
|
||||
use rustc_infer::traits::{self, ObligationCause};
|
||||
use rustc_infer::traits::{self, ObligationCause, PredicateObligations};
|
||||
use rustc_middle::traits::solve::Goal;
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
use rustc_span::Span;
|
||||
|
|
@ -15,10 +15,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// Returns a list of all obligations whose self type has been unified
|
||||
/// with the unconstrained type `self_ty`.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(crate) fn obligations_for_self_ty(
|
||||
&self,
|
||||
self_ty: ty::TyVid,
|
||||
) -> Vec<traits::PredicateObligation<'tcx>> {
|
||||
pub(crate) fn obligations_for_self_ty(&self, self_ty: ty::TyVid) -> PredicateObligations<'tcx> {
|
||||
if self.next_trait_solver() {
|
||||
self.obligations_for_self_ty_next(self_ty)
|
||||
} else {
|
||||
|
|
@ -75,10 +72,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
pub(crate) fn obligations_for_self_ty_next(
|
||||
&self,
|
||||
self_ty: ty::TyVid,
|
||||
) -> Vec<traits::PredicateObligation<'tcx>> {
|
||||
) -> PredicateObligations<'tcx> {
|
||||
let obligations = self.fulfillment_cx.borrow().pending_obligations();
|
||||
debug!(?obligations);
|
||||
let mut obligations_for_self_ty = vec![];
|
||||
let mut obligations_for_self_ty = PredicateObligations::new();
|
||||
for obligation in obligations {
|
||||
let mut visitor = NestedObligationsForSelfTy {
|
||||
fcx: self,
|
||||
|
|
@ -103,7 +100,7 @@ struct NestedObligationsForSelfTy<'a, 'tcx> {
|
|||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
self_ty: ty::TyVid,
|
||||
root_cause: &'a ObligationCause<'tcx>,
|
||||
obligations_for_self_ty: &'a mut Vec<traits::PredicateObligation<'tcx>>,
|
||||
obligations_for_self_ty: &'a mut PredicateObligations<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) =
|
||||
expr.kind
|
||||
&& let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr)
|
||||
&& self.can_coerce(recv_ty, expected)
|
||||
&& self.may_coerce(recv_ty, expected)
|
||||
&& let name = method.name.as_str()
|
||||
&& (name.starts_with("to_") || name.starts_with("as_") || name == "into")
|
||||
{
|
||||
|
|
@ -349,7 +349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return true;
|
||||
}
|
||||
|
||||
if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
|
||||
if self.suggest_fn_call(err, expr, found, |output| self.may_coerce(output, expected))
|
||||
&& let ty::FnDef(def_id, ..) = *found.kind()
|
||||
&& let Some(sp) = self.tcx.hir().span_if_local(def_id)
|
||||
{
|
||||
|
|
@ -568,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if self.tcx.hir().is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
|
||||
return false;
|
||||
}
|
||||
if self.can_coerce(Ty::new_box(self.tcx, found), expected) {
|
||||
if self.may_coerce(Ty::new_box(self.tcx, found), expected) {
|
||||
let suggest_boxing = match found.kind() {
|
||||
ty::Tuple(tuple) if tuple.is_empty() => {
|
||||
errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
|
||||
|
|
@ -663,7 +663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
match expected.kind() {
|
||||
ty::Adt(def, _) if Some(def.did()) == pin_did => {
|
||||
if self.can_coerce(pin_box_found, expected) {
|
||||
if self.may_coerce(pin_box_found, expected) {
|
||||
debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
|
||||
match found.kind() {
|
||||
ty::Adt(def, _) if def.is_box() => {
|
||||
|
|
@ -689,7 +689,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
true
|
||||
} else if self.can_coerce(pin_found, expected) {
|
||||
} else if self.may_coerce(pin_found, expected) {
|
||||
match found.kind() {
|
||||
ty::Adt(def, _) if def.is_box() => {
|
||||
err.help("use `Box::pin`");
|
||||
|
|
@ -701,7 +701,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
false
|
||||
}
|
||||
}
|
||||
ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
|
||||
ty::Adt(def, _) if def.is_box() && self.may_coerce(box_found, expected) => {
|
||||
// Check if the parent expression is a call to Pin::new. If it
|
||||
// is and we were expecting a Box, ergo Pin<Box<expected>>, we
|
||||
// can suggest Box::pin.
|
||||
|
|
@ -849,7 +849,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
hir::FnRetTy::Return(hir_ty) => {
|
||||
if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind
|
||||
// FIXME: account for RPITIT.
|
||||
&& let [hir::GenericBound::Trait(trait_ref, _)] = op_ty.bounds
|
||||
&& let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds
|
||||
&& let Some(hir::PathSegment { args: Some(generic_args), .. }) =
|
||||
trait_ref.trait_ref.path.segments.last()
|
||||
&& let [constraint] = generic_args.constraints
|
||||
|
|
@ -884,7 +884,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let ty = Binder::bind_with_vars(ty, bound_vars);
|
||||
let ty = self.normalize(hir_ty.span, ty);
|
||||
let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
|
||||
if self.can_coerce(expected, ty) {
|
||||
if self.may_coerce(expected, ty) {
|
||||
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
|
||||
span: hir_ty.span,
|
||||
expected,
|
||||
|
|
@ -1035,7 +1035,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// extract all bounds from the source code using their spans
|
||||
let all_matching_bounds_strs = predicates_from_where
|
||||
.filter_map(|bound| match bound {
|
||||
GenericBound::Trait(_, _) => {
|
||||
GenericBound::Trait(_) => {
|
||||
self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
|
||||
}
|
||||
_ => None,
|
||||
|
|
@ -1141,12 +1141,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
ty::Asyncness::No => ty,
|
||||
};
|
||||
let ty = self.normalize(expr.span, ty);
|
||||
self.can_coerce(found, ty)
|
||||
self.may_coerce(found, ty)
|
||||
}
|
||||
hir::FnRetTy::DefaultReturn(_) if in_closure => {
|
||||
self.ret_coercion.as_ref().map_or(false, |ret| {
|
||||
let ret_ty = ret.borrow().expected_ty();
|
||||
self.can_coerce(found, ret_ty)
|
||||
self.may_coerce(found, ret_ty)
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
|
|
@ -1510,7 +1510,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
provided_ty
|
||||
};
|
||||
|
||||
if !self.can_coerce(expected_ty, dummy_ty) {
|
||||
if !self.may_coerce(expected_ty, dummy_ty) {
|
||||
return;
|
||||
}
|
||||
let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
|
||||
|
|
@ -1534,7 +1534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
expected_ty: Ty<'tcx>,
|
||||
) {
|
||||
if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
|
||||
if self.can_coerce(blk_ty, *elem_ty)
|
||||
if self.may_coerce(blk_ty, *elem_ty)
|
||||
&& blk.stmts.is_empty()
|
||||
&& blk.rules == hir::BlockCheckMode::DefaultBlock
|
||||
{
|
||||
|
|
@ -1744,7 +1744,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if item_ty.has_param() {
|
||||
return false;
|
||||
}
|
||||
if self.can_coerce(item_ty, expected_ty) {
|
||||
if self.may_coerce(item_ty, expected_ty) {
|
||||
err.span_suggestion_verbose(
|
||||
segment.ident.span,
|
||||
format!("try referring to the associated const `{capitalized_name}` instead",),
|
||||
|
|
@ -1804,7 +1804,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
|
||||
&& !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
|
||||
// Check that we're in fact trying to clone into the expected type
|
||||
&& self.can_coerce(*pointee_ty, expected_ty)
|
||||
&& self.may_coerce(*pointee_ty, expected_ty)
|
||||
&& let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty])
|
||||
// And the expected type doesn't implement `Clone`
|
||||
&& !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
|
||||
|
|
@ -2022,7 +2022,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
} else {
|
||||
return false;
|
||||
};
|
||||
if is_ctor || !self.can_coerce(args.type_at(0), expected) {
|
||||
if is_ctor || !self.may_coerce(args.type_at(0), expected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -2293,7 +2293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
|
||||
|
||||
let sole_field_ty = sole_field.ty(self.tcx, args);
|
||||
if self.can_coerce(expr_ty, sole_field_ty) {
|
||||
if self.may_coerce(expr_ty, sole_field_ty) {
|
||||
let variant_path =
|
||||
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
|
||||
// FIXME #56861: DRYer prelude filtering
|
||||
|
|
@ -2401,7 +2401,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
let int_type = args.type_at(0);
|
||||
if !self.can_coerce(expr_ty, int_type) {
|
||||
if !self.may_coerce(expr_ty, int_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -2585,7 +2585,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
|
||||
}
|
||||
};
|
||||
if self.can_coerce(ref_ty, expected) {
|
||||
if self.may_coerce(ref_ty, expected) {
|
||||
let mut sugg_sp = sp;
|
||||
if let hir::ExprKind::MethodCall(segment, receiver, args, _) = expr.kind {
|
||||
let clone_trait =
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::{CtorOf, DefKind, Namespace};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::{self, InferOk};
|
||||
use rustc_infer::traits::PredicateObligations;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::ty::{
|
||||
|
|
@ -199,6 +200,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self_ty, segment, span, call_expr, self_expr, &pick, args,
|
||||
);
|
||||
|
||||
// NOTE: on the failure path, we also record the possibly-used trait methods
|
||||
// since an unused import warning is kinda distracting from the method error.
|
||||
for &import_id in &pick.import_ids {
|
||||
debug!("used_trait_import: {:?}", import_id);
|
||||
self.typeck_results.borrow_mut().used_trait_imports.insert(import_id);
|
||||
|
|
@ -412,7 +415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
|
||||
let mut obligations = vec![];
|
||||
let mut obligations = PredicateObligations::new();
|
||||
|
||||
// FIXME(effects): revisit when binops get `#[const_trait]`
|
||||
|
||||
|
|
|
|||
|
|
@ -191,6 +191,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
expected: Expectation<'tcx>,
|
||||
trait_missing_method: bool,
|
||||
) -> ErrorGuaranteed {
|
||||
// NOTE: Reporting a method error should also suppress any unused trait errors,
|
||||
// since the method error is very possibly the reason why the trait wasn't used.
|
||||
for &import_id in
|
||||
self.tcx.in_scope_traits(call_id).into_iter().flatten().flat_map(|c| &c.import_ids)
|
||||
{
|
||||
self.typeck_results.borrow_mut().used_trait_imports.insert(import_id);
|
||||
}
|
||||
|
||||
let (span, sugg_span, source, item_name, args) = match self.tcx.hir_node(call_id) {
|
||||
hir::Node::Expr(&hir::Expr {
|
||||
kind: hir::ExprKind::MethodCall(segment, rcvr, args, _),
|
||||
|
|
@ -952,7 +960,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
continue;
|
||||
}
|
||||
unimplemented_traits.entry(p.trait_ref.def_id).or_insert((
|
||||
predicate.kind().rebind(p.trait_ref),
|
||||
predicate.kind().rebind(p),
|
||||
Obligation {
|
||||
cause: cause.clone(),
|
||||
param_env: self.param_env,
|
||||
|
|
@ -1926,7 +1934,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
&& fn_sig.inputs()[1..]
|
||||
.iter()
|
||||
.zip(args.into_iter())
|
||||
.all(|(expected, found)| self.can_coerce(*expected, *found))
|
||||
.all(|(expected, found)| self.may_coerce(*expected, *found))
|
||||
&& fn_sig.inputs()[1..].len() == args.len()
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
|
|
@ -4140,7 +4148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return false;
|
||||
};
|
||||
|
||||
if !self.can_coerce(output, expected) {
|
||||
if !self.may_coerce(output, expected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -690,16 +690,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
BindingMode(def_br, Mutability::Mut)
|
||||
} else {
|
||||
// `mut` resets binding mode on edition <= 2021
|
||||
self.typeck_results
|
||||
// `mut` resets the binding mode on edition <= 2021
|
||||
*self
|
||||
.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.insert(pat_info.top_info.hir_id);
|
||||
.entry(pat_info.top_info.hir_id)
|
||||
.or_default() |= pat.span.at_least_rust_2024();
|
||||
BindingMode(ByRef::No, Mutability::Mut)
|
||||
}
|
||||
}
|
||||
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
|
||||
BindingMode(ByRef::Yes(_), _) => user_bind_annot,
|
||||
BindingMode(ByRef::Yes(_), _) => {
|
||||
if matches!(def_br, ByRef::Yes(_)) {
|
||||
// `ref`/`ref mut` overrides the binding mode on edition <= 2021
|
||||
*self
|
||||
.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.entry(pat_info.top_info.hir_id)
|
||||
.or_default() |= pat.span.at_least_rust_2024();
|
||||
}
|
||||
user_bind_annot
|
||||
}
|
||||
};
|
||||
|
||||
if bm.0 == ByRef::Yes(Mutability::Mut)
|
||||
|
|
@ -1767,7 +1780,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
} else if inexistent_fields.len() == 1 {
|
||||
match pat_field.pat.kind {
|
||||
PatKind::Lit(expr)
|
||||
if !self.can_coerce(
|
||||
if !self.may_coerce(
|
||||
self.typeck_results.borrow().expr_ty(expr),
|
||||
self.field_ty(field.span, field_def, args),
|
||||
) => {}
|
||||
|
|
@ -2204,14 +2217,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
} else {
|
||||
// Reset binding mode on old editions
|
||||
|
||||
if pat_info.binding_mode != ByRef::No {
|
||||
pat_info.binding_mode = ByRef::No;
|
||||
|
||||
self.typeck_results
|
||||
*self
|
||||
.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.insert(pat_info.top_info.hir_id);
|
||||
.entry(pat_info.top_info.hir_id)
|
||||
.or_default() |= pat.span.at_least_rust_2024();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2262,6 +2275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
(err, err)
|
||||
}
|
||||
};
|
||||
|
||||
self.check_pat(inner, inner_ty, pat_info);
|
||||
ref_ty
|
||||
}
|
||||
|
|
|
|||
|
|
@ -635,7 +635,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn visit_rust_2024_migration_desugared_pats(&mut self, hir_id: hir::HirId) {
|
||||
if self
|
||||
if let Some(is_hard_error) = self
|
||||
.fcx
|
||||
.typeck_results
|
||||
.borrow_mut()
|
||||
|
|
@ -645,7 +645,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
debug!(
|
||||
"node is a pat whose match ergonomics are desugared by the Rust 2024 migration lint"
|
||||
);
|
||||
self.typeck_results.rust_2024_migration_desugared_pats_mut().insert(hir_id);
|
||||
self.typeck_results
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.insert(hir_id, is_hard_error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "2.0.9", features = ["full"] }
|
||||
syn = { version = "2.0.9", features = ["full", "extra-traits"] }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -21,5 +21,6 @@ rustc_span = { path = "../rustc_span" }
|
|||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_type_ir = { path = "../rustc_type_ir" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
thin-vec = "0.2.12"
|
||||
tracing = "0.1"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ use crate::infer::region_constraints::{Constraint, RegionConstraintData};
|
|||
use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult};
|
||||
use crate::traits::query::NoSolution;
|
||||
use crate::traits::{
|
||||
Obligation, ObligationCause, PredicateObligation, ScrubbedTraitError, TraitEngine,
|
||||
Obligation, ObligationCause, PredicateObligation, PredicateObligations, ScrubbedTraitError,
|
||||
TraitEngine,
|
||||
};
|
||||
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
|
|
@ -493,7 +494,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
),
|
||||
};
|
||||
|
||||
let mut obligations = vec![];
|
||||
let mut obligations = PredicateObligations::new();
|
||||
|
||||
// Carry all newly resolved opaque types to the caller's scope
|
||||
for &(a, b) in &query_response.value.opaque_types {
|
||||
|
|
@ -598,7 +599,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
variables1: &OriginalQueryValues<'tcx>,
|
||||
variables2: impl Fn(BoundVar) -> GenericArg<'tcx>,
|
||||
) -> InferResult<'tcx, ()> {
|
||||
let mut obligations = vec![];
|
||||
let mut obligations = PredicateObligations::new();
|
||||
for (index, value1) in variables1.var_values.iter().enumerate() {
|
||||
let value2 = variables2(BoundVar::new(index));
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ use tracing::{debug, instrument};
|
|||
use type_variable::TypeVariableOrigin;
|
||||
|
||||
use crate::infer::region_constraints::UndoLog;
|
||||
use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine};
|
||||
use crate::traits::{
|
||||
self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine,
|
||||
};
|
||||
|
||||
pub mod at;
|
||||
pub mod canonical;
|
||||
|
|
@ -68,7 +70,7 @@ pub(crate) mod snapshot;
|
|||
mod type_variable;
|
||||
|
||||
/// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper
|
||||
/// around `Vec<PredicateObligation<'tcx>>`, but it has one important property:
|
||||
/// around `PredicateObligations<'tcx>`, but it has one important property:
|
||||
/// because `InferOk` is marked with `#[must_use]`, if you have a method
|
||||
/// `InferCtxt::f` that returns `InferResult<'tcx, ()>` and you call it with
|
||||
/// `infcx.f()?;` you'll get a warning about the obligations being discarded
|
||||
|
|
@ -78,7 +80,7 @@ mod type_variable;
|
|||
#[derive(Debug)]
|
||||
pub struct InferOk<'tcx, T> {
|
||||
pub value: T,
|
||||
pub obligations: Vec<PredicateObligation<'tcx>>,
|
||||
pub obligations: PredicateObligations<'tcx>,
|
||||
}
|
||||
pub type InferResult<'tcx, T> = Result<InferOk<'tcx, T>, TypeError<'tcx>>;
|
||||
|
||||
|
|
@ -658,7 +660,7 @@ impl<'tcx, T> InferOk<'tcx, T> {
|
|||
}
|
||||
|
||||
impl<'tcx> InferOk<'tcx, ()> {
|
||||
pub fn into_obligations(self) -> Vec<PredicateObligation<'tcx>> {
|
||||
pub fn into_obligations(self) -> PredicateObligations<'tcx> {
|
||||
self.obligations
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use tracing::{debug, instrument};
|
|||
use super::DefineOpaqueTypes;
|
||||
use crate::errors::OpaqueHiddenTypeDiag;
|
||||
use crate::infer::{InferCtxt, InferOk};
|
||||
use crate::traits::{self, Obligation};
|
||||
use crate::traits::{self, Obligation, PredicateObligations};
|
||||
|
||||
mod table;
|
||||
|
||||
|
|
@ -46,14 +46,14 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
) -> InferOk<'tcx, T> {
|
||||
// We handle opaque types differently in the new solver.
|
||||
if self.next_trait_solver() {
|
||||
return InferOk { value, obligations: vec![] };
|
||||
return InferOk { value, obligations: PredicateObligations::new() };
|
||||
}
|
||||
|
||||
if !value.has_opaque_types() {
|
||||
return InferOk { value, obligations: vec![] };
|
||||
return InferOk { value, obligations: PredicateObligations::new() };
|
||||
}
|
||||
|
||||
let mut obligations = vec![];
|
||||
let mut obligations = PredicateObligations::new();
|
||||
let value = value.fold_with(&mut BottomUpFolder {
|
||||
tcx: self.tcx,
|
||||
lt_op: |lt| lt,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use rustc_middle::traits::ObligationCause;
|
|||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
use super::InferCtxt;
|
||||
use crate::traits::{Obligation, PredicateObligation};
|
||||
use crate::traits::{Obligation, PredicateObligations};
|
||||
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// Instead of normalizing an associated type projection,
|
||||
|
|
@ -17,7 +17,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
projection_ty: ty::AliasTy<'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
recursion_depth: usize,
|
||||
obligations: &mut Vec<PredicateObligation<'tcx>>,
|
||||
obligations: &mut PredicateObligations<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert!(!self.next_trait_solver());
|
||||
let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id));
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use tracing::{debug, instrument};
|
|||
use super::StructurallyRelateAliases;
|
||||
use super::combine::PredicateEmittingRelation;
|
||||
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin, TypeTrace};
|
||||
use crate::traits::{Obligation, PredicateObligation};
|
||||
use crate::traits::{Obligation, PredicateObligations};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum LatticeOpKind {
|
||||
|
|
@ -52,7 +52,7 @@ pub(crate) struct LatticeOp<'infcx, 'tcx> {
|
|||
param_env: ty::ParamEnv<'tcx>,
|
||||
// Mutable fields
|
||||
kind: LatticeOpKind,
|
||||
obligations: Vec<PredicateObligation<'tcx>>,
|
||||
obligations: PredicateObligations<'tcx>,
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> LatticeOp<'infcx, 'tcx> {
|
||||
|
|
@ -62,10 +62,10 @@ impl<'infcx, 'tcx> LatticeOp<'infcx, 'tcx> {
|
|||
param_env: ty::ParamEnv<'tcx>,
|
||||
kind: LatticeOpKind,
|
||||
) -> LatticeOp<'infcx, 'tcx> {
|
||||
LatticeOp { infcx, trace, param_env, kind, obligations: vec![] }
|
||||
LatticeOp { infcx, trace, param_env, kind, obligations: PredicateObligations::new() }
|
||||
}
|
||||
|
||||
pub(crate) fn into_obligations(self) -> Vec<PredicateObligation<'tcx>> {
|
||||
pub(crate) fn into_obligations(self) -> PredicateObligations<'tcx> {
|
||||
self.obligations
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use tracing::{debug, instrument};
|
|||
use crate::infer::BoundRegionConversionTime::HigherRankedType;
|
||||
use crate::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases};
|
||||
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin, TypeTrace};
|
||||
use crate::traits::{Obligation, PredicateObligation};
|
||||
use crate::traits::{Obligation, PredicateObligations};
|
||||
|
||||
/// Enforce that `a` is equal to or a subtype of `b`.
|
||||
pub(crate) struct TypeRelating<'infcx, 'tcx> {
|
||||
|
|
@ -24,7 +24,7 @@ pub(crate) struct TypeRelating<'infcx, 'tcx> {
|
|||
|
||||
// Mutable fields.
|
||||
ambient_variance: ty::Variance,
|
||||
obligations: Vec<PredicateObligation<'tcx>>,
|
||||
obligations: PredicateObligations<'tcx>,
|
||||
/// The cache only tracks the `ambient_variance` as it's the
|
||||
/// only field which is mutable and which meaningfully changes
|
||||
/// the result when relating types.
|
||||
|
|
@ -65,12 +65,12 @@ impl<'infcx, 'tcx> TypeRelating<'infcx, 'tcx> {
|
|||
param_env,
|
||||
define_opaque_types,
|
||||
ambient_variance,
|
||||
obligations: vec![],
|
||||
obligations: PredicateObligations::new(),
|
||||
cache: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_obligations(self) -> Vec<PredicateObligation<'tcx>> {
|
||||
pub(crate) fn into_obligations(self) -> PredicateObligations<'tcx> {
|
||||
self.obligations
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::fmt::Debug;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::{self, Ty, Upcast};
|
||||
|
||||
use super::{ObligationCause, PredicateObligation};
|
||||
use super::{ObligationCause, PredicateObligation, PredicateObligations};
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::traits::Obligation;
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ pub enum ScrubbedTraitError<'tcx> {
|
|||
/// An ambiguity. This goal may hold if further inference is done.
|
||||
Ambiguity,
|
||||
/// An old-solver-style cycle error, which will fatal.
|
||||
Cycle(Vec<PredicateObligation<'tcx>>),
|
||||
Cycle(PredicateObligations<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> ScrubbedTraitError<'tcx> {
|
||||
|
|
@ -62,7 +62,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx {
|
|||
fn register_predicate_obligations(
|
||||
&mut self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
obligations: Vec<PredicateObligation<'tcx>>,
|
||||
obligations: PredicateObligations<'tcx>,
|
||||
) {
|
||||
for obligation in obligations {
|
||||
self.register_predicate_obligation(infcx, obligation);
|
||||
|
|
@ -84,7 +84,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx {
|
|||
self.collect_remaining_errors(infcx)
|
||||
}
|
||||
|
||||
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
|
||||
fn pending_obligations(&self) -> PredicateObligations<'tcx>;
|
||||
|
||||
/// Among all pending obligations, collect those are stalled on a inference variable which has
|
||||
/// changed since the last call to `select_where_possible`. Those obligations are marked as
|
||||
|
|
@ -92,7 +92,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx {
|
|||
fn drain_unstalled_obligations(
|
||||
&mut self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
) -> Vec<PredicateObligation<'tcx>>;
|
||||
) -> PredicateObligations<'tcx>;
|
||||
}
|
||||
|
||||
pub trait FromSolverError<'tcx, E>: Debug + 'tcx {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use rustc_middle::traits::solve::Certainty;
|
|||
pub use rustc_middle::traits::*;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
|
||||
use rustc_span::Span;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
pub use self::ImplSource::*;
|
||||
pub use self::SelectionError::*;
|
||||
|
|
@ -84,6 +85,8 @@ pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
|
|||
pub type TraitObligation<'tcx> = Obligation<'tcx, ty::TraitPredicate<'tcx>>;
|
||||
pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
|
||||
|
||||
pub type PredicateObligations<'tcx> = ThinVec<PredicateObligation<'tcx>>;
|
||||
|
||||
impl<'tcx> PredicateObligation<'tcx> {
|
||||
/// Flips the polarity of the inner predicate.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_middle::traits::EvaluationResult;
|
|||
use rustc_middle::ty;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use super::PredicateObligation;
|
||||
use super::PredicateObligations;
|
||||
use crate::infer::snapshot::undo_log::InferCtxtUndoLogs;
|
||||
|
||||
pub(crate) type UndoLog<'tcx> =
|
||||
|
|
@ -20,7 +20,7 @@ pub struct MismatchedProjectionTypes<'tcx> {
|
|||
#[derive(Clone)]
|
||||
pub struct Normalized<'tcx, T> {
|
||||
pub value: T,
|
||||
pub obligations: Vec<PredicateObligation<'tcx>>,
|
||||
pub obligations: PredicateObligations<'tcx>,
|
||||
}
|
||||
|
||||
pub type NormalizedTerm<'tcx> = Normalized<'tcx, ty::Term<'tcx>>;
|
||||
|
|
@ -191,7 +191,7 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
|
|||
info!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty);
|
||||
let mut ty = ty.clone();
|
||||
if result.must_apply_considering_regions() {
|
||||
ty.obligations = vec![];
|
||||
ty.obligations = PredicateObligations::new();
|
||||
}
|
||||
map.insert(key, ProjectionCacheEntry::NormalizedTerm {
|
||||
ty,
|
||||
|
|
|
|||
|
|
@ -810,7 +810,7 @@ fn test_unstable_options_tracking_hash() {
|
|||
tracked!(mir_opt_level, Some(4));
|
||||
tracked!(move_size_limit, Some(4096));
|
||||
tracked!(mutable_noalias, false);
|
||||
tracked!(next_solver, Some(NextSolverConfig { coherence: true, globally: false }));
|
||||
tracked!(next_solver, NextSolverConfig { coherence: true, globally: true });
|
||||
tracked!(no_generate_arange_section, true);
|
||||
tracked!(no_jump_tables, true);
|
||||
tracked!(no_link, true);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ declare_lint! {
|
|||
/// ### Example
|
||||
///
|
||||
/// ```rust,edition2021
|
||||
/// #![cfg_attr(not(bootstrap), feature(if_let_rescope))] // Simplify this in bootstrap bump.
|
||||
/// #![feature(if_let_rescope)]
|
||||
/// #![warn(if_let_rescope)]
|
||||
/// #![allow(unused_variables)]
|
||||
///
|
||||
|
|
|
|||
|
|
@ -112,10 +112,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
|
|||
|
||||
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
|
||||
let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return };
|
||||
for (bound, modifier) in &bounds[..] {
|
||||
for bound in &bounds[..] {
|
||||
let def_id = bound.trait_ref.trait_def_id();
|
||||
if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop))
|
||||
&& *modifier != hir::TraitBoundModifier::Maybe
|
||||
// FIXME: ?Drop is not a thing.
|
||||
&& bound.modifiers != hir::TraitBoundModifier::Maybe
|
||||
{
|
||||
let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
|
||||
cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
|
||||
|
|
|
|||
|
|
@ -1651,7 +1651,6 @@ declare_lint! {
|
|||
/// ### Example
|
||||
///
|
||||
/// ```rust,edition2021
|
||||
/// #![feature(ref_pat_eat_one_layer_2024)]
|
||||
/// #![warn(rust_2024_incompatible_pat)]
|
||||
///
|
||||
/// if let Some(&a) = &Some(&0u8) {
|
||||
|
|
@ -1672,12 +1671,10 @@ declare_lint! {
|
|||
pub RUST_2024_INCOMPATIBLE_PAT,
|
||||
Allow,
|
||||
"detects patterns whose meaning will change in Rust 2024",
|
||||
@feature_gate = ref_pat_eat_one_layer_2024;
|
||||
// FIXME uncomment below upon stabilization
|
||||
/*@future_incompatible = FutureIncompatibleInfo {
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
reference: "123076",
|
||||
};*/
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
|
|
@ -1871,7 +1868,6 @@ declare_lint! {
|
|||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// # #[cfg_attr(bootstrap)] compile_error!(); // Remove this in bootstrap bump.
|
||||
/// #![deny(elided_named_lifetimes)]
|
||||
/// struct Foo;
|
||||
/// impl Foo {
|
||||
|
|
@ -3859,7 +3855,7 @@ declare_lint! {
|
|||
/// This will produce:
|
||||
///
|
||||
/// ```text
|
||||
/// warning: use of calling convention not supported on this target on function pointer
|
||||
/// warning: the calling convention `"stdcall"` is not supported on this target
|
||||
/// --> $DIR/unsupported.rs:34:15
|
||||
/// |
|
||||
/// LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ type SwitchSources = FxHashMap<(BasicBlock, BasicBlock), SmallVec<[Option<u128>;
|
|||
struct Cache {
|
||||
predecessors: OnceLock<Predecessors>,
|
||||
switch_sources: OnceLock<SwitchSources>,
|
||||
is_cyclic: OnceLock<bool>,
|
||||
reverse_postorder: OnceLock<Vec<BasicBlock>>,
|
||||
dominators: OnceLock<Dominators<BasicBlock>>,
|
||||
}
|
||||
|
|
@ -37,12 +36,6 @@ impl<'tcx> BasicBlocks<'tcx> {
|
|||
BasicBlocks { basic_blocks, cache: Cache::default() }
|
||||
}
|
||||
|
||||
/// Returns true if control-flow graph contains a cycle reachable from the `START_BLOCK`.
|
||||
#[inline]
|
||||
pub fn is_cfg_cyclic(&self) -> bool {
|
||||
*self.cache.is_cyclic.get_or_init(|| graph::is_cyclic(self))
|
||||
}
|
||||
|
||||
pub fn dominators(&self) -> &Dominators<BasicBlock> {
|
||||
self.cache.dominators.get_or_init(|| dominators(self))
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue