Auto merge of #3975 - rust-lang:rustup-2024-10-17, r=RalfJung

Automatic Rustup
This commit is contained in:
bors 2024-10-17 06:34:09 +00:00
commit f7ccac9666
648 changed files with 9561 additions and 4426 deletions

View file

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

View file

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

View file

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

View file

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

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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(_), .. },
..
})
) {

View file

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

View file

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

View file

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

View file

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

View file

@ -295,6 +295,7 @@ pub(crate) struct FnQualifierInExtern {
pub span: Span,
#[label]
pub block: Span,
pub kw: &'static str,
}
#[derive(Diagnostic)]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -652,7 +652,6 @@ pub fn check_function_signature<'tcx>(
})),
err,
false,
false,
);
return Err(diag.emit());
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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