diff options
522 files changed, 9506 insertions, 3346 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6dc27f1234..003c1e5d7eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,6 +122,9 @@ jobs: # which then uses log commands to actually set them. EXTRA_VARIABLES: ${{ toJson(matrix.env) }} + - name: setup upstream remote + run: src/ci/scripts/setup-upstream-remote.sh + - name: ensure the channel matches the target branch run: src/ci/scripts/verify-channel.sh diff --git a/Cargo.lock b/Cargo.lock index c62c379f70d..701ffc0e7d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e" dependencies = [ - "unicode-width", + "unicode-width 0.1.14", "yansi-term", ] @@ -107,7 +107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24e35ed54e5ea7997c14ed4c70ba043478db1112e98263b3b035907aa197d991" dependencies = [ "anstyle", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -168,7 +168,7 @@ dependencies = [ "anstyle", "anstyle-lossy", "html-escape", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -377,7 +377,7 @@ dependencies = [ "cargo_metadata", "directories", "rustc-build-sysroot", - "rustc_tools_util", + "rustc_tools_util 0.4.0", "rustc_version", "serde", "serde_json", @@ -552,7 +552,7 @@ dependencies = [ "parking_lot", "quote", "regex", - "rustc_tools_util", + "rustc_tools_util 0.3.0", "serde", "serde_json", "syn 2.0.79", @@ -737,7 +737,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.14", "windows-sys 0.52.0", ] @@ -1425,7 +1425,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -1788,7 +1788,7 @@ dependencies = [ "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -2590,7 +2590,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" dependencies = [ - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -2601,7 +2601,7 @@ checksum = "9ad43c07024ef767f9160710b3a6773976194758c7919b17e63b863db0bdf7fb" dependencies = [ "bytecount", "fnv", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -4188,7 +4188,7 @@ dependencies = [ "thin-vec", "tracing", "unicode-normalization", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] @@ -4422,7 +4422,7 @@ dependencies = [ "sha1", "sha2", "tracing", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] @@ -4466,6 +4466,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f" [[package]] +name = "rustc_tools_util" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3316159ab19e19d1065ecc49278e87f767a9dae9fae80348d2b4d4fa4ae02d4d" + +[[package]] name = "rustc_trait_selection" version = "0.0.0" dependencies = [ @@ -4687,7 +4693,7 @@ dependencies = [ "tracing-subscriber", "unicode-properties", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -5097,7 +5103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c998b0c8b921495196a48aabaf1901ff28be0760136e31604f7967b0792050e" dependencies = [ "papergrid", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -5647,6 +5653,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5894,7 +5906,7 @@ dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width", + "unicode-width 0.1.14", "wasm-encoder 0.219.0", ] diff --git a/RELEASES.md b/RELEASES.md index b49470c3075..1213a596024 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,180 @@ +Version 1.82.0 (2024-10-17) +========================== + +<a id="1.82.0-Language"></a> + +Language +-------- +- [Don't make statement nonterminals match pattern nonterminals](https://github.com/rust-lang/rust/pull/120221/) +- [Patterns matching empty types can now be omitted in common cases](https://github.com/rust-lang/rust/pull/122792) +- [Enforce supertrait outlives obligations when using trait impls](https://github.com/rust-lang/rust/pull/124336) +- [`addr_of(_mut)!` macros and the newly stabilized `&raw (const|mut)` are now safe to use with all static items](https://github.com/rust-lang/rust/pull/125834) +- [size_of_val_raw: for length 0 this is safe to call](https://github.com/rust-lang/rust/pull/126152/) +- [Reorder trait bound modifiers *after* `for<...>` binder in trait bounds](https://github.com/rust-lang/rust/pull/127054/) +- [Stabilize opaque type precise capturing (RFC 3617)](https://github.com/rust-lang/rust/pull/127672) +- [Stabilize `&raw const` and `&raw mut` operators (RFC 2582)](https://github.com/rust-lang/rust/pull/127679) +- [Stabilize unsafe extern blocks (RFC 3484)](https://github.com/rust-lang/rust/pull/127921) +- [Stabilize nested field access in `offset_of!`](https://github.com/rust-lang/rust/pull/128284) +- [Do not require `T` to be live when dropping `[T; 0]`](https://github.com/rust-lang/rust/pull/128438) +- [Stabilize `const` operands in inline assembly](https://github.com/rust-lang/rust/pull/128570) +- [Stabilize floating-point arithmetic in `const fn`](https://github.com/rust-lang/rust/pull/128596) +- [Stabilize explicit opt-in to unsafe attributes](https://github.com/rust-lang/rust/pull/128771) +- [Document NaN bit patterns guarantees](https://github.com/rust-lang/rust/pull/129559) + + +<a id="1.82.0-Compiler"></a> + +Compiler +-------- +- [Promote riscv64gc-unknown-linux-musl to tier 2](https://github.com/rust-lang/rust/pull/122049) +- [Promote Mac Catalyst targets `aarch64-apple-ios-macabi` and `x86_64-apple-ios-macabi` to Tier 2, and ship them with rustup](https://github.com/rust-lang/rust/pull/126450) +- [Add tier 3 NuttX based targets for RISC-V and ARM](https://github.com/rust-lang/rust/pull/127755) +- [Add tier 3 powerpc-unknown-linux-muslspe target](https://github.com/rust-lang/rust/pull/127905) +- [Improved diagnostics to explain why a pattern is unreachable](https://github.com/rust-lang/rust/pull/128034) +- [The compiler now triggers the unreachable code warning properly for async functions that don't return/are `-> !`](https://github.com/rust-lang/rust/pull/128443) +- [Promote `aarch64-apple-darwin` to Tier 1](https://github.com/rust-lang/rust/pull/128592) +- [Add Trusty OS target `aarch64-unknown-trusty` and `armv7-unknown-trusty` as tier 3 targets](https://github.com/rust-lang/rust/pull/129490) +- [Promote `wasm32-wasip2` to Tier 2.](https://github.com/rust-lang/rust/pull/126967/) + + +<a id="1.82.0-Libraries"></a> + +Libraries +--------- +- [Generalize `{Rc,Arc}::make_mut()` to `Path`, `OsStr`, and `CStr`.](https://github.com/rust-lang/rust/pull/126877) + +<a id="1.82.0-Stabilized-APIs"></a> + +Stabilized APIs +--------------- + +- [`std::thread::Builder::spawn_unchecked`](https://doc.rust-lang.org/stable/std/thread/struct.Builder.html#method.spawn_unchecked) +- [`std::str::CharIndices::offset`](https://doc.rust-lang.org/nightly/std/str/struct.CharIndices.html#method.offset) +- [`std::option::Option::is_none_or`](https://doc.rust-lang.org/nightly/std/option/enum.Option.html#method.is_none_or) +- [`[T]::is_sorted`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.is_sorted) +- [`[T]::is_sorted_by`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.is_sorted_by) +- [`[T]::is_sorted_by_key`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.is_sorted_by_key) +- [`Iterator::is_sorted`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.is_sorted) +- [`Iterator::is_sorted_by`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.is_sorted_by) +- [`Iterator::is_sorted_by_key`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.is_sorted_by_key) +- [`std::future::Ready::into_inner`](https://doc.rust-lang.org/nightly/std/future/struct.Ready.html#method.into_inner) +- [`std::iter::repeat_n`](https://doc.rust-lang.org/nightly/std/iter/fn.repeat_n.html) +- [`impl<T: Clone> DoubleEndedIterator for Take<Repeat<T>>`](https://doc.rust-lang.org/nightly/std/iter/struct.Take.html#impl-DoubleEndedIterator-for-Take%3CRepeat%3CT%3E%3E) +- [`impl<T: Clone> ExactSizeIterator for Take<Repeat<T>>`](https://doc.rust-lang.org/nightly/std/iter/struct.Take.html#impl-ExactSizeIterator-for-Take%3CRepeat%3CT%3E%3E) +- [`impl<T: Clone> ExactSizeIterator for Take<RepeatWith<T>>`](https://doc.rust-lang.org/nightly/std/iter/struct.Take.html#impl-ExactSizeIterator-for-Take%3CRepeatWith%3CF%3E%3E) +- [`impl Default for std::collections::binary_heap::Iter`](https://doc.rust-lang.org/nightly/std/collections/binary_heap/struct.Iter.html#impl-Default-for-Iter%3C'_,+T%3E) +- [`impl Default for std::collections::btree_map::RangeMut`](https://doc.rust-lang.org/nightly/std/collections/btree_map/struct.RangeMut.html#impl-Default-for-RangeMut%3C'_,+K,+V%3E) +- [`impl Default for std::collections::btree_map::ValuesMut`](https://doc.rust-lang.org/nightly/std/collections/btree_map/struct.ValuesMut.html#impl-Default-for-ValuesMut%3C'_,+K,+V%3E) +- [`impl Default for std::collections::vec_deque::Iter`](https://doc.rust-lang.org/nightly/std/collections/vec_deque/struct.Iter.html#impl-Default-for-Iter%3C'_,+T%3E) +- [`impl Default for std::collections::vec_deque::IterMut`](https://doc.rust-lang.org/nightly/std/collections/vec_deque/struct.IterMut.html#impl-Default-for-IterMut%3C'_,+T%3E) +- [`Rc<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new_uninit) +- [`Rc<T>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init) +- [`Rc<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new_uninit_slice) +- [`Rc<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init-1) +- [`Arc<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.new_uninit) +- [`Arc<T>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init) +- [`Arc<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.new_uninit_slice) +- [`Arc<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init-1) +- [`Box<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.new_uninit) +- [`Box<T>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init) +- [`Box<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.new_uninit_slice) +- [`Box<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init-1) +- [`core::arch::x86_64::_bextri_u64`](https://doc.rust-lang.org/stable/core/arch/x86_64/fn._bextri_u64.html) +- [`core::arch::x86_64::_bextri_u32`](https://doc.rust-lang.org/stable/core/arch/x86_64/fn._bextri_u32.html) +- [`core::arch::x86::_mm_broadcastsi128_si256`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_broadcastsi128_si256.html) +- [`core::arch::x86::_mm256_stream_load_si256`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm256_stream_load_si256.html) +- [`core::arch::x86::_tzcnt_u16`](https://doc.rust-lang.org/stable/core/arch/x86/fn._tzcnt_u16.html) +- [`core::arch::x86::_mm_extracti_si64`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_extracti_si64.html) +- [`core::arch::x86::_mm_inserti_si64`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_inserti_si64.html) +- [`core::arch::x86::_mm_storeu_si16`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_storeu_si16.html) +- [`core::arch::x86::_mm_storeu_si32`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_storeu_si32.html) +- [`core::arch::x86::_mm_storeu_si64`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_storeu_si64.html) +- [`core::arch::x86::_mm_loadu_si16`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_loadu_si16.html) +- [`core::arch::x86::_mm_loadu_si32`](https://doc.rust-lang.org/stable/core/arch/x86/fn._mm_loadu_si32.html) +- [`core::arch::wasm32::u8x16_relaxed_swizzle`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u8x16_relaxed_swizzle.html) +- [`core::arch::wasm32::i8x16_relaxed_swizzle`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i8x16_relaxed_swizzle.html) +- [`core::arch::wasm32::i32x4_relaxed_trunc_f32x4`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i32x4_relaxed_trunc_f32x4.html) +- [`core::arch::wasm32::u32x4_relaxed_trunc_f32x4`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u32x4_relaxed_trunc_f32x4.html) +- [`core::arch::wasm32::i32x4_relaxed_trunc_f64x2_zero`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i32x4_relaxed_trunc_f64x2_zero.html) +- [`core::arch::wasm32::u32x4_relaxed_trunc_f64x2_zero`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u32x4_relaxed_trunc_f64x2_zero.html) +- [`core::arch::wasm32::f32x4_relaxed_madd`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f32x4_relaxed_madd.html) +- [`core::arch::wasm32::f32x4_relaxed_nmadd`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f32x4_relaxed_nmadd.html) +- [`core::arch::wasm32::f64x2_relaxed_madd`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f64x2_relaxed_madd.html) +- [`core::arch::wasm32::f64x2_relaxed_nmadd`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f64x2_relaxed_nmadd.html) +- [`core::arch::wasm32::i8x16_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i8x16_relaxed_laneselect.html) +- [`core::arch::wasm32::u8x16_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u8x16_relaxed_laneselect.html) +- [`core::arch::wasm32::i16x8_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i16x8_relaxed_laneselect.html) +- [`core::arch::wasm32::u16x8_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u16x8_relaxed_laneselect.html) +- [`core::arch::wasm32::i32x4_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i32x4_relaxed_laneselect.html) +- [`core::arch::wasm32::u32x4_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u32x4_relaxed_laneselect.html) +- [`core::arch::wasm32::i64x2_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i64x2_relaxed_laneselect.html) +- [`core::arch::wasm32::u64x2_relaxed_laneselect`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u64x2_relaxed_laneselect.html) +- [`core::arch::wasm32::f32x4_relaxed_min`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f32x4_relaxed_min.html) +- [`core::arch::wasm32::f32x4_relaxed_max`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f32x4_relaxed_max.html) +- [`core::arch::wasm32::f64x2_relaxed_min`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f64x2_relaxed_min.html) +- [`core::arch::wasm32::f64x2_relaxed_max`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.f64x2_relaxed_max.html) +- [`core::arch::wasm32::i16x8_relaxed_q15mulr`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i16x8_relaxed_q15mulr.html) +- [`core::arch::wasm32::u16x8_relaxed_q15mulr`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u16x8_relaxed_q15mulr.html) +- [`core::arch::wasm32::i16x8_relaxed_dot_i8x16_i7x16`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i16x8_relaxed_dot_i8x16_i7x16.html) +- [`core::arch::wasm32::u16x8_relaxed_dot_i8x16_i7x16`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u16x8_relaxed_dot_i8x16_i7x16.html) +- [`core::arch::wasm32::i32x4_relaxed_dot_i8x16_i7x16_add`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.i32x4_relaxed_dot_i8x16_i7x16_add.html) +- [`core::arch::wasm32::u32x4_relaxed_dot_i8x16_i7x16_add`](https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.u32x4_relaxed_dot_i8x16_i7x16_add.html) + +These APIs are now stable in const contexts: + +- [`std::task::Waker::from_raw`](https://doc.rust-lang.org/nightly/std/task/struct.Waker.html#method.from_raw) +- [`std::task::Waker::waker`](https://doc.rust-lang.org/nightly/std/task/struct.Waker.html#method.from_raw) +- [`std::task::Context::from_waker`](https://doc.rust-lang.org/nightly/std/task/struct.Context.html#method.from_waker) +- [`std::task::Context::waker`](https://doc.rust-lang.org/nightly/std/task/struct.Context.html#method.waker) +- [`$integer::from_str_radix`](https://doc.rust-lang.org/nightly/std/primitive.u32.html#method.from_str_radix) +- [`std::num::ParseIntError::kind`](https://doc.rust-lang.org/nightly/std/num/struct.ParseIntError.html#method.kind) + +<a id="1.82.0-Cargo"></a> + +Cargo +----- +- [feat: Add `info` cargo subcommand](https://github.com/rust-lang/cargo/pull/14141/) + +<a id="1.82.0-Compatibility-Notes"></a> + +Compatibility Notes +------------------- + - We now [disallow setting some built-in cfgs via the command-line](https://github.com/rust-lang/rust/pull/126158) with the newly added [`explicit_builtin_cfgs_in_flags`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#explicit-builtin-cfgs-in-flags) lint in order to prevent incoherent state, eg. `windows` cfg active but target is Linux based. The appropriate [`rustc` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html) should be used instead. +- The standard library has a new implementation of `binary_search` which is significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation. +- [illumos/Solaris now sets `MSG_NOSIGNAL` when writing to sockets](https://github.com/rust-lang/rust/pull/128259). This avoids killing the process with SIGPIPE when writing to a closed socket, which matches the existing behavior on other UNIX targets. +- [Removes a problematic hack that always passed the --whole-archive linker flag for tests, which may cause linker errors for code accidentally relying on it.](https://github.com/rust-lang/rust/pull/128400) +- The WebAssembly target features `multivalue` and `reference-types` are now + both enabled by default. These two features both have subtle changes implied + for generated WebAssembly binaries. For the `multivalue` feature, WebAssembly + target support has changed when upgrading to LLVM 19. Support for generating + functions with multiple returns no longer works and + `-Ctarget-feature=+multivalue` has a different meaning than it did in LLVM 18 + and prior. There is no longer any supported means to generate a module that has + a function with multiple returns in WebAssembly from Rust source code. For the + `reference-types` feature the encoding of immediates in the `call_indirect`, a + commonly used instruction by the WebAssembly backend, has changed. Validators + and parsers which don't understand the `reference-types` proposal will no + longer accept modules produced by LLVM due to this change in encoding of + immediates. Additionally these features being enabled are encoded in the + `target_features` custom section and may affect downstream tooling such as + `wasm-opt` consuming the module. Generating a WebAssembly module that disables + default features requires `-Zbuild-std` support from Cargo and more information + can be found at + [rust-lang/rust#128511](https://github.com/rust-lang/rust/pull/128511). +- [Rust now raises unsafety errors for union patterns in parameter-position](https://github.com/rust-lang/rust/pull/130531) + + +<a id="1.82.0-Internal-Changes"></a> + +Internal Changes +---------------- + +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. + +- [Update to LLVM 19](https://github.com/rust-lang/rust/pull/127513) + Version 1.81.0 (2024-09-05) ========================== diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs new file mode 100644 index 00000000000..2ecac8a9df1 --- /dev/null +++ b/compiler/rustc_abi/src/callconv.rs @@ -0,0 +1,254 @@ +mod abi { + pub(crate) use crate::Primitive::*; + pub(crate) use crate::Variants; +} + +use rustc_macros::HashStable_Generic; + +use crate::{Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum RegKind { + Integer, + Float, + Vector, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub struct Reg { + pub kind: RegKind, + pub size: Size, +} + +macro_rules! reg_ctor { + ($name:ident, $kind:ident, $bits:expr) => { + pub fn $name() -> Reg { + Reg { kind: RegKind::$kind, size: Size::from_bits($bits) } + } + }; +} + +impl Reg { + reg_ctor!(i8, Integer, 8); + reg_ctor!(i16, Integer, 16); + reg_ctor!(i32, Integer, 32); + reg_ctor!(i64, Integer, 64); + reg_ctor!(i128, Integer, 128); + + reg_ctor!(f32, Float, 32); + reg_ctor!(f64, Float, 64); +} + +impl Reg { + pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { + let dl = cx.data_layout(); + match self.kind { + RegKind::Integer => match self.size.bits() { + 1 => dl.i1_align.abi, + 2..=8 => dl.i8_align.abi, + 9..=16 => dl.i16_align.abi, + 17..=32 => dl.i32_align.abi, + 33..=64 => dl.i64_align.abi, + 65..=128 => dl.i128_align.abi, + _ => panic!("unsupported integer: {self:?}"), + }, + RegKind::Float => match self.size.bits() { + 16 => dl.f16_align.abi, + 32 => dl.f32_align.abi, + 64 => dl.f64_align.abi, + 128 => dl.f128_align.abi, + _ => panic!("unsupported float: {self:?}"), + }, + RegKind::Vector => dl.vector_align(self.size).abi, + } + } +} + +/// Return value from the `homogeneous_aggregate` test function. +#[derive(Copy, Clone, Debug)] +pub enum HomogeneousAggregate { + /// Yes, all the "leaf fields" of this struct are passed in the + /// same way (specified in the `Reg` value). + Homogeneous(Reg), + + /// There are no leaf fields at all. + NoData, +} + +/// Error from the `homogeneous_aggregate` test function, indicating +/// there are distinct leaf fields passed in different ways, +/// or this is uninhabited. +#[derive(Copy, Clone, Debug)] +pub struct Heterogeneous; + +impl HomogeneousAggregate { + /// If this is a homogeneous aggregate, returns the homogeneous + /// unit, else `None`. + pub fn unit(self) -> Option<Reg> { + match self { + HomogeneousAggregate::Homogeneous(reg) => Some(reg), + HomogeneousAggregate::NoData => None, + } + } + + /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in + /// the same `struct`. Only succeeds if only one of them has any data, + /// or both units are identical. + fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> { + match (self, other) { + (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x), + + (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => { + if a != b { + return Err(Heterogeneous); + } + Ok(self) + } + } + } +} + +impl<'a, Ty> TyAndLayout<'a, Ty> { + /// Returns `true` if this is an aggregate type (including a ScalarPair!) + pub fn is_aggregate(&self) -> bool { + match self.abi { + Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, + Abi::ScalarPair(..) | Abi::Aggregate { .. } => true, + } + } + + /// Returns `Homogeneous` if this layout is an aggregate containing fields of + /// only a single type (e.g., `(u32, u32)`). Such aggregates are often + /// special-cased in ABIs. + /// + /// Note: We generally ignore 1-ZST fields when computing this value (see #56877). + /// + /// This is public so that it can be used in unit tests, but + /// should generally only be relevant to the ABI details of + /// specific targets. + pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous> + where + Ty: TyAbiInterface<'a, C> + Copy, + { + match self.abi { + Abi::Uninhabited => Err(Heterogeneous), + + // The primitive for this algorithm. + Abi::Scalar(scalar) => { + let kind = match scalar.primitive() { + abi::Int(..) | abi::Pointer(_) => RegKind::Integer, + abi::Float(_) => RegKind::Float, + }; + Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size })) + } + + Abi::Vector { .. } => { + assert!(!self.is_zst()); + Ok(HomogeneousAggregate::Homogeneous(Reg { + kind: RegKind::Vector, + size: self.size, + })) + } + + Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => { + // Helper for computing `homogeneous_aggregate`, allowing a custom + // starting offset (used below for handling variants). + let from_fields_at = + |layout: Self, + start: Size| + -> Result<(HomogeneousAggregate, Size), Heterogeneous> { + let is_union = match layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } + FieldsShape::Array { count, .. } => { + assert_eq!(start, Size::ZERO); + + let result = if count > 0 { + layout.field(cx, 0).homogeneous_aggregate(cx)? + } else { + HomogeneousAggregate::NoData + }; + return Ok((result, layout.size)); + } + FieldsShape::Union(_) => true, + FieldsShape::Arbitrary { .. } => false, + }; + + let mut result = HomogeneousAggregate::NoData; + let mut total = start; + + for i in 0..layout.fields.count() { + let field = layout.field(cx, i); + if field.is_1zst() { + // No data here and no impact on layout, can be ignored. + // (We might be able to also ignore all aligned ZST but that's less clear.) + continue; + } + + if !is_union && total != layout.fields.offset(i) { + // This field isn't just after the previous one we considered, abort. + return Err(Heterogeneous); + } + + result = result.merge(field.homogeneous_aggregate(cx)?)?; + + // Keep track of the offset (without padding). + let size = field.size; + if is_union { + total = total.max(size); + } else { + total += size; + } + } + + Ok((result, total)) + }; + + let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?; + + match &self.variants { + abi::Variants::Single { .. } => {} + abi::Variants::Multiple { variants, .. } => { + // Treat enum variants like union members. + // HACK(eddyb) pretend the `enum` field (discriminant) + // is at the start of every variant (otherwise the gap + // at the start of all variants would disqualify them). + // + // NB: for all tagged `enum`s (which include all non-C-like + // `enum`s with defined FFI representation), this will + // match the homogeneous computation on the equivalent + // `struct { tag; union { variant1; ... } }` and/or + // `union { struct { tag; variant1; } ... }` + // (the offsets of variant fields should be identical + // between the two for either to be a homogeneous aggregate). + let variant_start = total; + for variant_idx in variants.indices() { + let (variant_result, variant_total) = + from_fields_at(self.for_variant(cx, variant_idx), variant_start)?; + + result = result.merge(variant_result)?; + total = total.max(variant_total); + } + } + } + + // There needs to be no padding. + if total != self.size { + Err(Heterogeneous) + } else { + match result { + HomogeneousAggregate::Homogeneous(_) => { + assert_ne!(total, Size::ZERO); + } + HomogeneousAggregate::NoData => { + assert_eq!(total, Size::ZERO); + } + } + Ok(result) + } + } + Abi::Aggregate { sized: false } => Err(Heterogeneous), + } + } +} diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 620a051eeb7..6e1299944a0 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -11,6 +11,10 @@ use crate::{ Variants, WrappingRange, }; +mod ty; + +pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; + // A variant is absent if it's uninhabited and only has ZST fields. // Present uninhabited variants only require space for their fields, // but *not* an encoding of the discriminant (e.g., a tag value). diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_abi/src/layout/ty.rs index b744d5ad4ed..c6812c4d4c0 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -6,18 +6,8 @@ use Primitive::*; use rustc_data_structures::intern::Interned; use rustc_macros::HashStable_Generic; -use crate::json::{Json, ToJson}; - -pub mod call; - // Explicitly import `Float` to avoid ambiguity with `Primitive::Float`. -pub use rustc_abi::{Float, *}; - -impl ToJson for Endian { - fn to_json(&self) -> Json { - self.as_str().to_json() - } -} +use crate::{Float, *}; rustc_index::newtype_index! { /// The *source-order* index of a field in a variant. diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index fa7c98a7fa0..84d756b6d51 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1,6 +1,7 @@ // tidy-alphabetical-start #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", doc(rust_logo))] +#![cfg_attr(feature = "nightly", feature(rustc_attrs))] #![cfg_attr(feature = "nightly", feature(rustdoc_internals))] #![cfg_attr(feature = "nightly", feature(step_trait))] #![warn(unreachable_pub)] @@ -22,11 +23,16 @@ use rustc_macros::HashStable_Generic; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_Generic, Encodable_Generic}; +mod callconv; mod layout; #[cfg(test)] mod tests; -pub use layout::{LayoutCalculator, LayoutCalculatorError}; +pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind}; +pub use layout::{ + FIRST_VARIANT, FieldIdx, Layout, LayoutCalculator, LayoutCalculatorError, TyAbiInterface, + TyAndLayout, VariantIdx, +}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 937031324f5..02cb6f188a7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -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, } diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs new file mode 100644 index 00000000000..05714731b9d --- /dev/null +++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs @@ -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) + } +} diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs index 13413281bc7..d259677e98e 100644 --- a/compiler/rustc_ast/src/expand/mod.rs +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -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> { diff --git a/compiler/rustc_ast/src/expand/typetree.rs b/compiler/rustc_ast/src/expand/typetree.rs new file mode 100644 index 00000000000..9a2dd2e85e0 --- /dev/null +++ b/compiler/rustc_ast/src/expand/typetree.rs @@ -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) + } +} diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 1a7da46913d..2afbd979c30 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -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); diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index a517f17c82c..ae1ca36a3ba 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -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, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 4dcadb8517e..207ec710650 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -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); diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index eeed5d36151..ff9d940ce9f 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -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, +} diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 52372bbf991..ae1e1b3f8a2 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -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( diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 1273b50dff8..ce744cc56e1 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -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(_), .. }, .. }) ) { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 263e4a9379f..00eafeb4d84 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -55,8 +55,8 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalDefIdMap}; use rustc_hir::{ - self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, MissingLifetimeKind, ParamName, - TraitCandidate, + self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, MissingLifetimeKind, + ParamName, TraitCandidate, }; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::extension; @@ -765,8 +765,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { res } - fn make_lang_item_qpath(&mut self, lang_item: hir::LangItem, span: Span) -> hir::QPath<'hir> { - hir::QPath::Resolved(None, self.make_lang_item_path(lang_item, span, None)) + fn make_lang_item_qpath( + &mut self, + lang_item: hir::LangItem, + span: Span, + args: Option<&'hir hir::GenericArgs<'hir>>, + ) -> hir::QPath<'hir> { + hir::QPath::Resolved(None, self.make_lang_item_path(lang_item, span, args)) } fn make_lang_item_path( @@ -1219,13 +1224,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let bound = this.lower_poly_trait_ref( &PolyTraitRef { bound_generic_params: ThinVec::new(), + modifiers: TraitBoundModifiers::NONE, trait_ref: TraitRef { path: path.clone(), ref_id: t.id }, span: t.span, }, itctx, - TraitBoundModifiers::NONE, ); - let bound = (bound, hir::TraitBoundModifier::None); let bounds = this.arena.alloc_from_iter([bound]); let lifetime_bound = this.elided_dyn_bound(t.span); (bounds, lifetime_bound) @@ -1277,6 +1281,32 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let lifetime = self.lower_lifetime(®ion); hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx)) } + TyKind::PinnedRef(region, mt) => { + let region = region.unwrap_or_else(|| { + let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = + self.resolver.get_lifetime_res(t.id) + { + debug_assert_eq!(start.plus(1), end); + start + } else { + self.next_node_id() + }; + let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi(); + Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id } + }); + let lifetime = self.lower_lifetime(®ion); + let kind = hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx)); + let span = self.lower_span(t.span); + let arg = hir::Ty { kind, span, hir_id: self.next_id() }; + let args = self.arena.alloc(hir::GenericArgs { + args: self.arena.alloc([hir::GenericArg::Type(self.arena.alloc(arg))]), + constraints: &[], + parenthesized: hir::GenericArgsParentheses::No, + span_ext: span, + }); + let path = self.make_lang_item_qpath(LangItem::Pin, span, Some(args)); + hir::TyKind::Path(path) + } TyKind::BareFn(f) => { let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy { @@ -1326,16 +1356,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // We can safely ignore constness here since AST validation // takes care of rejecting invalid modifier combinations and // const trait bounds in trait object types. - GenericBound::Trait(ty, modifiers) => { - // Still, don't pass along the constness here; we don't want to - // synthesize any host effect args, it'd only cause problems. - let modifiers = TraitBoundModifiers { - constness: BoundConstness::Never, - ..*modifiers - }; - let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers); - let polarity = this.lower_trait_bound_modifiers(modifiers); - Some((trait_ref, polarity)) + GenericBound::Trait(ty) => { + let trait_ref = this.lower_poly_trait_ref(ty, itctx); + Some(trait_ref) } GenericBound::Outlives(lifetime) => { if lifetime_bound.is_none() { @@ -1851,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, } }), @@ -1964,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))] @@ -1988,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)) } @@ -2195,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> { @@ -2640,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. diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs index 1e82ba5db8a..fe64160fb4d 100644 --- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs +++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs @@ -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); } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 7cfbd6c6c6c..92acaaa5f36 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -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 diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 87e3a6871b9..20a4f2120dc 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -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); } } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index c60925b3049..4ca1acde1e2 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -295,6 +295,7 @@ pub(crate) struct FnQualifierInExtern { pub span: Span, #[label] pub block: Span, + pub kw: &'static str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 82de83f8da4..05850ca3260 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -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) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 44a5a2d0baf..2cdec2138ad 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -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), diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 98417e8c7a3..3871816777c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -706,7 +706,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { suggestions.push(( pat_span, format!("consider removing the {to_remove}"), - suggestion.to_string(), + suggestion, )); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 39175b406a4..6333d59a1bc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -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 diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 1a5f9bdb154..b4b8373ac97 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -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 diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index f3207c26bfc..3674053409b 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -165,7 +165,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( universe_causes, type_tests, liveness_constraints, - elements.clone(), + elements, ); // If requested: dump NLL facts, and run legacy polonius analysis. diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index 21b87be4b81..ef48486f6f1 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -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 diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 77cb8dc63c4..6ebc2fd870c 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -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` diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs new file mode 100644 index 00000000000..66bb11ca522 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -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; diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs index fab1906eecd..731945f5cbf 100644 --- a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs +++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs @@ -312,7 +312,7 @@ pub(crate) fn expand_deriving_smart_ptr( impl_generics.params.insert(pointee_param_idx + 1, extra_param); // Add the impl blocks for `DispatchFromDyn` and `CoerceUnsized`. - let gen_args = vec![GenericArg::Type(alt_self_type.clone())]; + let gen_args = vec![GenericArg::Type(alt_self_type)]; add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone()); add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args); } @@ -333,12 +333,12 @@ fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee: } fn is_maybe_sized_bound(bound: &GenericBound) -> bool { - if let GenericBound::Trait( - trait_ref, - TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. }, - ) = bound + if let GenericBound::Trait(trait_ref) = bound + && let TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. } = + trait_ref.modifiers + && is_sized_marker(&trait_ref.trait_ref.path) { - is_sized_marker(&trait_ref.trait_ref.path) + true } else { false } diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 25583cda5a2..43e2bf1796f 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -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) ]) diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 639c2aa231c..f8e65661e52 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -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 { diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 9501e93bad5..32730ac3867 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -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", diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index ebe5e2b5442..377d7f542cf 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -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, diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index d7b03a43ecb..2a28dfaf3c4 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -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`. diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 82ca3f519f7..d1d7d0cf4ce 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -154,7 +154,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // We prefer the latter because it matches the behavior of // Clang. if late && matches!(reg, InlineAsmRegOrRegClass::Reg(_)) { - constraints.push(reg_to_llvm(reg, Some(&in_value.layout)).to_string()); + constraints.push(reg_to_llvm(reg, Some(&in_value.layout))); } else { constraints.push(format!("{}", op_idx[&idx])); } @@ -542,57 +542,16 @@ fn xmm_reg_index(reg: InlineAsmReg) -> Option<u32> { /// If the register is an AArch64 integer register then return its index. fn a64_reg_index(reg: InlineAsmReg) -> Option<u32> { - use AArch64InlineAsmReg::*; - // Unlike `a64_vreg_index`, we can't subtract `x0` to get the u32 because - // `x19` and `x29` are missing and the integer constants for the - // `x0`..`x30` enum variants don't all match the register number. E.g. the - // integer constant for `x18` is 18, but the constant for `x20` is 19. - Some(match reg { - InlineAsmReg::AArch64(r) => match r { - x0 => 0, - x1 => 1, - x2 => 2, - x3 => 3, - x4 => 4, - x5 => 5, - x6 => 6, - x7 => 7, - x8 => 8, - x9 => 9, - x10 => 10, - x11 => 11, - x12 => 12, - x13 => 13, - x14 => 14, - x15 => 15, - x16 => 16, - x17 => 17, - x18 => 18, - // x19 is reserved - x20 => 20, - x21 => 21, - x22 => 22, - x23 => 23, - x24 => 24, - x25 => 25, - x26 => 26, - x27 => 27, - x28 => 28, - // x29 is reserved - x30 => 30, - _ => return None, - }, - _ => return None, - }) + match reg { + InlineAsmReg::AArch64(r) => r.reg_index(), + _ => None, + } } /// If the register is an AArch64 vector register then return its index. fn a64_vreg_index(reg: InlineAsmReg) -> Option<u32> { - use AArch64InlineAsmReg::*; match reg { - InlineAsmReg::AArch64(reg) if reg as u32 >= v0 as u32 && reg as u32 <= v31 as u32 => { - Some(reg as u32 - v0 as u32) - } + InlineAsmReg::AArch64(reg) => reg.vreg_index(), _ => None, } } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 4ca19ab2f12..0ced37b53a8 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -5,6 +5,7 @@ use rustc_abi as abi; use rustc_abi::Primitive::Pointer; use rustc_abi::{AddressSpace, HasDataLayout}; use rustc_ast::Mutability; +use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; use rustc_hir::def_id::DefId; @@ -146,6 +147,10 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn const_int(&self, t: &'ll Type, i: i64) -> &'ll Value { + debug_assert!( + self.type_kind(t) == TypeKind::Integer, + "only allows integer types in const_int" + ); unsafe { llvm::LLVMConstInt(t, i as u64, True) } } @@ -176,10 +181,18 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn const_uint(&self, t: &'ll Type, i: u64) -> &'ll Value { + debug_assert!( + self.type_kind(t) == TypeKind::Integer, + "only allows integer types in const_uint" + ); unsafe { llvm::LLVMConstInt(t, i, False) } } fn const_uint_big(&self, t: &'ll Type, u: u128) -> &'ll Value { + debug_assert!( + self.type_kind(t) == TypeKind::Integer, + "only allows integer types in const_uint_big" + ); unsafe { let words = [u as u64, (u >> 64) as u64]; llvm::LLVMConstIntOfArbitraryPrecision(t, 2, words.as_ptr()) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 4aec74595bc..2db43a0f787 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -140,7 +140,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> { #[inline(always)] fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> { - self.iter().filter_map(move |(k, v)| f(k, &*v)).collect() + self.iter().filter_map(move |(k, v)| f(k, v)).collect() } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index e6ab8ca12a8..7700eb792ef 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -993,11 +993,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { bytes } - /// Find leaked allocations. Allocations reachable from `static_roots` or a `Global` allocation - /// are not considered leaked, as well as leaks whose kind's `may_leak()` returns true. - pub fn find_leaked_allocations( - &self, - static_roots: &[AllocId], + /// Find leaked allocations, remove them from memory and return them. Allocations reachable from + /// `static_roots` or a `Global` allocation are not considered leaked, as well as leaks whose + /// kind's `may_leak()` returns true. + /// + /// This is highly destructive, no more execution can happen after this! + pub fn take_leaked_allocations( + &mut self, + static_roots: impl FnOnce(&Self) -> &[AllocId], ) -> Vec<(AllocId, MemoryKind<M::MemoryKind>, Allocation<M::Provenance, M::AllocExtra, M::Bytes>)> { // Collect the set of allocations that are *reachable* from `Global` allocations. @@ -1008,7 +1011,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.memory.alloc_map.filter_map_collect(move |&id, &(kind, _)| { if Some(kind) == global_kind { Some(id) } else { None } }); - todo.extend(static_roots); + todo.extend(static_roots(self)); while let Some(id) = todo.pop() { if reachable.insert(id) { // This is a new allocation, add the allocation it points to `todo`. @@ -1023,13 +1026,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking. - self.memory.alloc_map.filter_map_collect(|id, (kind, alloc)| { - if kind.may_leak() || reachable.contains(id) { - None - } else { - Some((*id, *kind, alloc.clone())) - } - }) + let leaked: Vec<_> = self.memory.alloc_map.filter_map_collect(|&id, &(kind, _)| { + if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) } + }); + let mut result = Vec::new(); + for &id in leaked.iter() { + let (kind, alloc) = self.memory.alloc_map.remove(&id).unwrap(); + result.push((id, kind, alloc)); + } + result } /// Runs the closure in "validation" mode, which means the machine's memory read hooks will be diff --git a/compiler/rustc_data_structures/src/unhash.rs b/compiler/rustc_data_structures/src/unhash.rs index 48e21a9dab1..0617ef83aad 100644 --- a/compiler/rustc_data_structures/src/unhash.rs +++ b/compiler/rustc_data_structures/src/unhash.rs @@ -3,6 +3,7 @@ use std::hash::{BuildHasherDefault, Hasher}; pub type UnhashMap<K, V> = HashMap<K, V, BuildHasherDefault<Unhasher>>; pub type UnhashSet<V> = HashSet<V, BuildHasherDefault<Unhasher>>; +pub type UnindexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<Unhasher>>; /// This no-op hasher expects only a single `write_u64` call. It's intended for /// map keys that already have hash-like quality, like `Fingerprint`. diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index b5945759d43..a673e2e3250 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -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, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 17827b4e43b..477760a4597 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -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: diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2ef6fa53f4e..009c6c4aea5 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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>, diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 58916d05865..ffe519b0e7d 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -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); diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index da814cd2d69..a9f30ffd6da 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -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 diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index dd13c3d8fc1..94da3d4ea84 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -8,7 +8,9 @@ use rustc_hir::Node; use rustc_hir::def::{CtorKind, DefKind}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::Obligation; -use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; +use rustc_lint_defs::builtin::{ + REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, +}; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; use rustc_middle::span_bug; @@ -52,16 +54,18 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { }); } } +} - // This ABI is only allowed on function pointers - if abi == Abi::CCmseNonSecureCall { - struct_span_code_err!( - tcx.dcx(), - span, - E0781, - "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers" - ) - .emit(); +pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { + match tcx.sess.target.is_abi_supported(abi) { + Some(true) => (), + Some(false) | None => { + tcx.node_span_lint(UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, hir_id, span, |lint| { + lint.primary_message(format!( + "the calling convention {abi} is not supported on this target" + )); + }); + } } } diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index d3d88919d87..004540b2643 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -73,7 +73,7 @@ pub mod wfcheck; use std::num::NonZero; -pub use check::check_abi; +pub use check::{check_abi, check_abi_fn_ptr}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 3a9d2640eee..607068f162a 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -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, }, diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index d1c888a185e..5127e73d978 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -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)); diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 6d30f7c7b9d..a87b29b3093 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -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) diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index c8852a3a369..cb7f0901c7e 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -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( diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 9099703e812..af4445a7fd4 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -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, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 45cd46e3df2..8f7ca089c91 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -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) } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 5150db7f51b..672dc8ddeda 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -1,9 +1,8 @@ -use rustc_errors::DiagCtxtHandle; -use rustc_hir as hir; -use rustc_hir::HirId; +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; @@ -18,49 +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 { - // might happen when this ABI is used incorrectly. That will be handled elsewhere - 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); + 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; + }; + + 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::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_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_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 }); + } + } + }; + } + _ => (), } } @@ -141,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 } } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 394a263fbb5..98822eec2ac 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -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) }); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index a735b8cc2a4..01768c89cca 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -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?; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 5607fe873f6..fd49e7e4439 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -1,6 +1,6 @@ use rustc_ast::TraitObjectSyntax; use rustc_errors::codes::*; -use rustc_errors::{Diag, EmissionGuarantee, StashKey}; +use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, StashKey, Suggestions}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::Applicability; @@ -15,13 +15,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. /// In edition 2021 and onward we emit a hard error for them. - pub(super) fn prohibit_or_lint_bare_trait_object_ty(&self, self_ty: &hir::Ty<'_>) { + pub(super) fn prohibit_or_lint_bare_trait_object_ty( + &self, + self_ty: &hir::Ty<'_>, + ) -> Option<ErrorGuaranteed> { let tcx = self.tcx(); let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = self_ty.kind else { - return; + return None; }; let in_path = match tcx.parent_hir_node(self_ty.hir_id) { @@ -47,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(), @@ -70,8 +73,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } if self_ty.span.edition().at_least_rust_2021() { - let msg = "trait objects must include the `dyn` keyword"; - let label = "add `dyn` keyword before this trait"; + let msg = "expected a type, found a trait"; + let label = "you can add the `dyn` keyword if you want a trait object"; let mut diag = rustc_errors::struct_span_code_err!(self.dcx(), self_ty.span, E0782, "{}", msg); if self_ty.span.can_be_used_for_suggestions() @@ -83,7 +86,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Check if the impl trait that we are considering is an impl of a local trait. self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag); self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag); - diag.stash(self_ty.span, StashKey::TraitMissingMethod); + // In case there is an associate type with the same name + // Add the suggestion to this error + if let Some(mut sugg) = + tcx.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion) + && let Suggestions::Enabled(ref mut s1) = diag.suggestions + && let Suggestions::Enabled(ref mut s2) = sugg.suggestions + { + s1.append(s2); + sugg.cancel(); + } + diag.stash(self_ty.span, StashKey::TraitMissingMethod) } else { tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, |lint| { lint.primary_message("trait objects without an explicit `dyn` are deprecated"); @@ -96,6 +109,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } self.maybe_suggest_blanket_trait_impl(self_ty, lint); }); + None } } @@ -174,41 +188,31 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // 1. Independent functions // 2. Functions inside trait blocks // 3. Functions inside impl blocks - let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) { + let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => { - (sig, generics, None) + (sig, generics) } hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(sig, _), generics, - owner_id, .. - }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), + }) => (sig, generics), hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(sig, _), generics, - owner_id, .. - }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), + }) => (sig, generics), _ => return false, }; let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { return false; }; let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; - let mut is_downgradable = true; - // 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 { - Res::Def(DefKind::Trait, id) => { - if Some(id) == owner { - // For recursive traits, don't downgrade the error. (#119652) - is_downgradable = false; - } - tcx.is_dyn_compatible(id) - } + objects.iter().all(|o| match o.trait_ref.path.res { + Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id), _ => false, }) } @@ -255,9 +259,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { suggestion, Applicability::MachineApplicable, ); - } else if is_downgradable { - // We'll emit the dyn-compatibility error already, with a structured suggestion. - diag.downgrade_to_delayed_bug(); } return true; } @@ -281,10 +282,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); if !is_dyn_compatible { diag.note(format!("`{trait_name}` it is dyn-incompatible, so it can't be `dyn`")); - if is_downgradable { - // We'll emit the dyn-compatibility error already, with a structured suggestion. - diag.downgrade_to_delayed_bug(); - } } else { // No ampersand in suggestion if it's borrowed already let (dyn_str, paren_dyn_str) = diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 28a1fc88741..d760acf53bd 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -53,6 +53,7 @@ use rustc_trait_selection::traits::{self, ObligationCtxt}; use tracing::{debug, debug_span, instrument}; use crate::bounds::Bounds; +use crate::check::check_abi_fn_ptr; use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, WildPatTy}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; @@ -2063,13 +2064,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) } hir::TyKind::TraitObject(bounds, lifetime, repr) => { - self.prohibit_or_lint_bare_trait_object_ty(hir_ty); - - let repr = match repr { - TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn, - TraitObjectSyntax::DynStar => ty::DynStar, - }; - self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr) + if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { + // Don't continue with type analysis if the `dyn` keyword is missing + // It generates confusing errors, especially if the user meant to use another + // keyword like `impl` + Ty::new_error(tcx, guar) + } else { + let repr = match repr { + TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn, + TraitObjectSyntax::DynStar => ty::DynStar, + }; + self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr) + } } // If we encounter a fully qualified path with RTN generics, then it must have // *not* gone through `lower_ty_maybe_return_type_notation`, and therefore @@ -2337,6 +2343,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi); let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); + if let hir::Node::Ty(hir::Ty { kind: hir::TyKind::BareFn(bare_fn_ty), span, .. }) = + tcx.hir_node(hir_id) + { + check_abi_fn_ptr(tcx, hir_id, *span, bare_fn_ty.abi); + } + // reject function types that violate cmse ABI requirements cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, bare_fn_ty); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 9fe6a8ee342..9ebfd4f15ab 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -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) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 1df4d32f3cb..3e9e5326156 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -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 @@ -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, diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index cb8b1df2c6e..1b65dc8214c 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -199,6 +199,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); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 4f726f3ed38..05c8912aec1 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -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, _), diff --git a/compiler/rustc_index_macros/Cargo.toml b/compiler/rustc_index_macros/Cargo.toml index 07ee81788ce..a7c2a1804dd 100644 --- a/compiler/rustc_index_macros/Cargo.toml +++ b/compiler/rustc_index_macros/Cargo.toml @@ -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" diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 6fecddb3319..3c31b879bd6 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -301,9 +301,13 @@ fn did_has_local_parent( return false; }; - peel_parent_while(tcx, parent_did, |tcx, did| tcx.def_kind(did) == DefKind::Mod) - .map(|parent_did| parent_did == impl_parent || Some(parent_did) == outermost_impl_parent) - .unwrap_or(false) + peel_parent_while(tcx, parent_did, |tcx, did| { + tcx.def_kind(did) == DefKind::Mod + || (tcx.def_kind(did) == DefKind::Const + && tcx.opt_item_name(did) == Some(kw::Underscore)) + }) + .map(|parent_did| parent_did == impl_parent || Some(parent_did) == outermost_impl_parent) + .unwrap_or(false) } /// Given a `DefId` checks if it satisfies `f` if it does check with it's parent and continue diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index c0a01b0065e..5a3666dcbd4 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -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 }); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 827791c54be..da603df9a9a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -124,6 +124,7 @@ declare_lint_pass! { UNSTABLE_NAME_COLLISIONS, UNSTABLE_SYNTAX_PRE_EXPANSION, UNSUPPORTED_CALLING_CONVENTIONS, + UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, UNUSED_ASSIGNMENTS, UNUSED_ASSOCIATED_TYPE_BOUNDS, UNUSED_ATTRIBUTES, @@ -3840,6 +3841,50 @@ declare_lint! { } declare_lint! { + /// The `unsupported_fn_ptr_calling_conventions` lint is output whenever there is a use of + /// a target dependent calling convention on a target that does not support this calling + /// convention on a function pointer. + /// + /// For example `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc + /// code, because this calling convention was never specified for those targets. + /// + /// ### Example + /// + /// ```rust,ignore (needs specific targets) + /// fn stdcall_ptr(f: extern "stdcall" fn ()) { + /// f() + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: the calling convention `"stdcall"` is not supported on this target + /// --> $DIR/unsupported.rs:34:15 + /// | + /// LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + /// | ^^^^^^^^^^^^^^^^^^^^^^^^ + /// | + /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + /// = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + /// = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// On most of the targets the behaviour of `stdcall` and similar calling conventions is not + /// defined at all, but was previously accepted due to a bug in the implementation of the + /// compiler. + pub UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, + Warn, + "use of unsupported calling convention for function pointer", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reference: "issue #130260 <https://github.com/rust-lang/rust/issues/130260>", + }; +} + +declare_lint! { /// The `break_with_label_and_loop` lint detects labeled `break` expressions with /// an unlabeled loop as their value expression. /// diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 70e61df1ab4..e9b73d25ba2 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -37,7 +37,6 @@ #![feature(box_as_ptr)] #![feature(box_patterns)] #![feature(closure_track_caller)] -#![feature(const_option)] #![feature(const_type_name)] #![feature(core_intrinsics)] #![feature(coroutines)] diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 431043b0431..fcb87e19435 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -754,6 +754,7 @@ impl Drop for Guard { /// /// We also make things panic if this type is ever implicitly dropped. #[derive(Debug)] +#[must_use] pub struct InterpResult_<'tcx, T> { res: Result<T, InterpErrorInfo<'tcx>>, guard: Guard, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 50e7be82a79..1bdcae4dfb4 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -12,6 +12,7 @@ use std::marker::PhantomData; use std::ops::{Bound, Deref}; use std::{fmt, iter, mem}; +use rustc_abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}; use rustc_ast::{self as ast, attr}; use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; @@ -48,7 +49,6 @@ use rustc_session::{Limit, MetadataKind, Session}; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{DUMMY_SP, Span}; -use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}; use rustc_target::spec::abi; use rustc_type_ir::TyKind::*; use rustc_type_ir::fold::TypeFoldable; diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 751f0c71eb4..4f408ee1574 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -192,7 +192,8 @@ fn suggest_changing_unsized_bound( .iter() .enumerate() .filter(|(_, bound)| { - if let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound + if let hir::GenericBound::Trait(poly) = bound + && poly.modifiers == hir::TraitBoundModifier::Maybe && poly.trait_ref.trait_def_id() == def_id { true @@ -325,7 +326,7 @@ pub fn suggest_constraining_type_params<'a>( let suggestion = if span_to_replace.is_some() { constraint.clone() } else if constraint.starts_with('<') { - constraint.to_string() + constraint.clone() } else if bound_list_non_empty { format!(" + {constraint}") } else { diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 458edf920ab..daacc669118 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -4,6 +4,7 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; +use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; @@ -18,9 +19,9 @@ use crate::delegate::SolverDelegate; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; use crate::solve::{ - CanonicalInput, CanonicalResponse, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind, - GoalSource, HasChanged, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, - QueryResult, SolverMode, + CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind, GoalSource, + HasChanged, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryResult, + SolverMode, }; pub(super) mod canonical; @@ -987,40 +988,22 @@ where // Do something for each opaque/hidden pair defined with `def_id` in the // current inference context. - pub(super) fn unify_existing_opaque_tys( + pub(super) fn probe_existing_opaque_ty( &mut self, - param_env: I::ParamEnv, key: ty::OpaqueTypeKey<I>, - ty: I::Ty, - ) -> Vec<CanonicalResponse<I>> { - // FIXME: Super inefficient to be cloning this... - let opaques = self.delegate.clone_opaque_types_for_query_response(); - - let mut values = vec![]; - for (candidate_key, candidate_ty) in opaques { - if candidate_key.def_id != key.def_id { - continue; - } - values.extend( - self.probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup { - result: *result, - }) - .enter(|ecx| { - for (a, b) in std::iter::zip(candidate_key.args.iter(), key.args.iter()) { - ecx.eq(param_env, a, b)?; - } - ecx.eq(param_env, candidate_ty, ty)?; - ecx.add_item_bounds_for_hidden_type( - candidate_key.def_id.into(), - candidate_key.args, - param_env, - candidate_ty, - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }), + ) -> Option<(ty::OpaqueTypeKey<I>, I::Ty)> { + let mut matching = + self.delegate.clone_opaque_types_for_query_response().into_iter().filter( + |(candidate_key, _)| { + candidate_key.def_id == key.def_id + && DeepRejectCtxt::relate_rigid_rigid(self.cx()) + .args_may_unify(candidate_key.args, key.args) + }, ); - } - values + let first = matching.next(); + let second = matching.next(); + assert_eq!(second, None); + first } // Try to evaluate a const, or return `None` if the const is too generic. diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 6a0703c5313..f8d51f304f3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -7,7 +7,9 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::{self as ty, Interner}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, Reveal, SolverMode}; +use crate::solve::{ + Certainty, EvalCtxt, Goal, NoSolution, QueryResult, Reveal, SolverMode, inspect, +}; impl<D, I> EvalCtxt<'_, D> where @@ -52,14 +54,28 @@ where // // If that fails, we insert `expected` as a new hidden type instead of // eagerly emitting an error. - let matches = - self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected); - if !matches.is_empty() { - if let Some(response) = self.try_merge_responses(&matches) { - return Ok(response); - } else { - return self.flounder(&matches); - } + let existing = self.probe_existing_opaque_ty(opaque_type_key); + if let Some((candidate_key, candidate_ty)) = existing { + return self + .probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup { + result: *result, + }) + .enter(|ecx| { + for (a, b) in std::iter::zip( + candidate_key.args.iter(), + opaque_type_key.args.iter(), + ) { + ecx.eq(goal.param_env, a, b)?; + } + ecx.eq(goal.param_env, candidate_ty, expected)?; + ecx.add_item_bounds_for_hidden_type( + candidate_key.def_id.into(), + candidate_key.args, + goal.param_env, + candidate_ty, + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }); } // Otherwise, define a new opaque type diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index c59ae48a07d..2360914a0ab 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -20,7 +20,7 @@ rustc_span = { path = "../rustc_span" } thin-vec = "0.2.12" tracing = "0.1" unicode-normalization = "0.1.11" -unicode-width = "0.1.4" +unicode-width = "0.2.0" # tidy-alphabetical-end [dev-dependencies] diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index ba5e2ddf4fc..1af7fb9b86a 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -422,6 +422,9 @@ parse_invalid_meta_item = expected unsuffixed literal, found `{$token}` parse_invalid_offset_of = offset_of expects dot-separated field and variant names +parse_invalid_path_sep_in_fn_definition = invalid path separator in function definition + .suggestion = remove invalid path separator + parse_invalid_unicode_escape = invalid unicode character escape .label = invalid escape .help = unicode escape must {$surrogate -> diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 124975f67f1..fdd500e90f8 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1756,6 +1756,14 @@ pub(crate) struct MissingFnParams { } #[derive(Diagnostic)] +#[diag(parse_invalid_path_sep_in_fn_definition)] +pub(crate) struct InvalidPathSepInFnDefinition { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(parse_missing_trait_in_trait_impl)] pub(crate) struct MissingTraitInTraitImpl { #[primary_span] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index f32307f6ed4..a9384501547 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2113,7 +2113,7 @@ impl<'a> Parser<'a> { && let Some(poly) = bounds .iter() .filter_map(|bound| match bound { - ast::GenericBound::Trait(poly, _) => Some(poly), + ast::GenericBound::Trait(poly) => Some(poly), _ => None, }) .last() diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index b9256daa725..5aebe716b0a 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -269,6 +269,13 @@ impl<'a> Parser<'a> { /// | ( < lifetimes , typaramseq ( , )? > ) /// where typaramseq = ( typaram ) | ( typaram , typaramseq ) pub(super) fn parse_generics(&mut self) -> PResult<'a, ast::Generics> { + // invalid path separator `::` in function definition + // for example `fn invalid_path_separator::<T>() {}` + if self.eat_noexpect(&token::PathSep) { + self.dcx() + .emit_err(errors::InvalidPathSepInFnDefinition { span: self.prev_token.span }); + } + let span_lo = self.token.span; let (params, span) = if self.eat_lt() { let params = self.parse_generic_params()?; diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 162ff3b94de..2f19a9b6b20 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -948,8 +948,8 @@ impl<'a> Parser<'a> { { return Ok((false, seg.ident, seg.args.as_deref().cloned())); } else if let ast::TyKind::TraitObject(bounds, ast::TraitObjectSyntax::None) = &ty.kind - && let [ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifiers::NONE)] = - bounds.as_slice() + && let [ast::GenericBound::Trait(trait_ref)] = bounds.as_slice() + && trait_ref.modifiers == ast::TraitBoundModifiers::NONE && let [seg] = trait_ref.trait_ref.path.segments.as_slice() { return Ok((true, seg.ident, seg.args.as_deref().cloned())); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 625a4cabdf2..c561ea3823d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -4,7 +4,8 @@ use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, - PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, + Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, + TyKind, }; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::{Ident, kw, sym}; @@ -419,8 +420,13 @@ impl<'a> Parser<'a> { lo: Span, parse_plus: bool, ) -> PResult<'a, TyKind> { - let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_token.span)); - let bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifiers::NONE)]; + let poly_trait_ref = PolyTraitRef::new( + generic_params, + path, + TraitBoundModifiers::NONE, + lo.to(self.prev_token.span), + ); + let bounds = vec![GenericBound::Trait(poly_trait_ref)]; self.parse_remaining_bounds(bounds, parse_plus) } @@ -487,7 +493,10 @@ impl<'a> Parser<'a> { fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { let and_span = self.prev_token.span; let mut opt_lifetime = self.check_lifetime().then(|| self.expect_lifetime()); - let mut mutbl = self.parse_mutability(); + let (pinned, mut mutbl) = match self.parse_pin_and_mut() { + Some(pin_mut) => pin_mut, + None => (Pinnedness::Not, self.parse_mutability()), + }; if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() { // A lifetime is invalid here: it would be part of a bare trait bound, which requires // it to be followed by a plus, but we disallow plus in the pointee type. @@ -523,7 +532,35 @@ impl<'a> Parser<'a> { self.bump_with((dyn_tok, dyn_tok_sp)); } let ty = self.parse_ty_no_plus()?; - Ok(TyKind::Ref(opt_lifetime, MutTy { ty, mutbl })) + Ok(match pinned { + Pinnedness::Not => TyKind::Ref(opt_lifetime, MutTy { ty, mutbl }), + Pinnedness::Pinned => TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl }), + }) + } + + /// Parses `pin` and `mut` annotations on references. + /// + /// It must be either `pin const` or `pin mut`. + pub(crate) fn parse_pin_and_mut(&mut self) -> Option<(Pinnedness, Mutability)> { + if self.token.is_ident_named(sym::pin) { + let result = self.look_ahead(1, |token| { + if token.is_keyword(kw::Const) { + Some((Pinnedness::Pinned, Mutability::Not)) + } else if token.is_keyword(kw::Mut) { + Some((Pinnedness::Pinned, Mutability::Mut)) + } else { + None + } + }); + if result.is_some() { + self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span); + self.bump(); + self.bump(); + } + result + } else { + None + } } // Parses the `typeof(EXPR)`. @@ -1085,8 +1122,9 @@ impl<'a> Parser<'a> { } } - let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_token.span)); - Ok(GenericBound::Trait(poly_trait, modifiers)) + let poly_trait = + PolyTraitRef::new(lifetime_defs, path, modifiers, lo.to(self.prev_token.span)); + Ok(GenericBound::Trait(poly_trait)) } // recovers a `Fn(..)` parenthesized-style path from `fn(..)` diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index e7ef4385baf..1716f417969 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -473,19 +473,19 @@ impl<'a> Parser<'a> { } pos = peek_pos; - description = format!("expected `'}}'`, found `{maybe:?}`"); + description = format!("expected `}}`, found `{}`", maybe.escape_debug()); } else { - description = "expected `'}'` but string was terminated".to_owned(); + description = "expected `}` but string was terminated".to_owned(); // point at closing `"` pos = self.input.len() - if self.append_newline { 1 } else { 0 }; } let pos = self.to_span_index(pos); - let label = "expected `'}'`".to_owned(); + let label = "expected `}`".to_owned(); let (note, secondary_label) = if arg.format.fill == Some('}') { ( - Some("the character `'}'` is interpreted as a fill character because of the `:` that precedes it".to_owned()), + Some("the character `}` is interpreted as a fill character because of the `:` that precedes it".to_owned()), arg.format.fill_span.map(|sp| ("this is not interpreted as a formatting closing brace".to_owned(), sp)), ) } else { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index be76d6cef2b..e5a14f6a156 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -49,6 +49,10 @@ passes_attr_crate_level = passes_attr_only_in_functions = `{$attr}` attribute can only be used on functions +passes_autodiff_attr = + `#[autodiff]` should be applied to a function + .label = not a function + passes_both_ffi_const_and_pure = `#[ffi_const]` function cannot be `#[ffi_pure]` @@ -739,6 +743,12 @@ passes_unrecognized_repr_hint = unrecognized representation hint .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` +passes_unstable_attr_for_already_stable_feature = + can't mark as unstable using an already stable feature + .label = this feature is already stable + .item = the stability attribute annotates this item + .help = consider removing the attribute + passes_unused = unused attribute .suggestion = remove this attribute diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 44a62383e6e..7ce29260e36 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -243,6 +243,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_generic_attr(hir_id, attr, target, Target::Fn); self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) } + [sym::autodiff, ..] => { + self.check_autodiff(hir_id, attr, span, target) + } [sym::coroutine, ..] => { self.check_coroutine(attr, target); } @@ -2345,6 +2348,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span }); } } + + /// Checks if `#[autodiff]` is applied to an item other than a function item. + fn check_autodiff(&self, _hir_id: HirId, _attr: &Attribute, span: Span, target: Target) { + debug!("check_autodiff"); + match target { + Target::Fn => {} + _ => { + self.dcx().emit_err(errors::AutoDiffAttr { attr_span: span }); + self.abort.set(true); + } + } + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 4f00c90fa3b..6dc3dfba58f 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -20,6 +20,14 @@ use crate::lang_items::Duplicate; #[diag(passes_incorrect_do_not_recommend_location)] pub(crate) struct IncorrectDoNotRecommendLocation; +#[derive(Diagnostic)] +#[diag(passes_autodiff_attr)] +pub(crate) struct AutoDiffAttr { + #[primary_span] + #[label] + pub attr_span: Span, +} + #[derive(LintDiagnostic)] #[diag(passes_outer_crate_level_attr)] pub(crate) struct OuterCrateLevelAttr; @@ -1481,6 +1489,17 @@ pub(crate) struct CannotStabilizeDeprecated { } #[derive(Diagnostic)] +#[diag(passes_unstable_attr_for_already_stable_feature)] +pub(crate) struct UnstableAttrForAlreadyStableFeature { + #[primary_span] + #[label] + #[help] + pub span: Span, + #[label(passes_item)] + pub item_sp: Span, +} + +#[derive(Diagnostic)] #[diag(passes_missing_stability_attr)] pub(crate) struct MissingStabilityAttr<'a> { #[primary_span] diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index b5dccf85041..a4c3d789176 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -579,6 +579,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Array, Ptr, Ref, + PinnedRef, BareFn, Never, Tup, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 46b67e930d5..751c87a9fe5 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -10,6 +10,7 @@ use rustc_attr::{ }; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; +use rustc_feature::ACCEPTED_FEATURES; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; @@ -246,12 +247,27 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + if let Stability { level: Unstable { .. }, feature } = stab { + if ACCEPTED_FEATURES.iter().find(|f| f.name == feature).is_some() { + self.tcx + .dcx() + .emit_err(errors::UnstableAttrForAlreadyStableFeature { span, item_sp }); + } + } if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab { self.index.implications.insert(implied_by, feature); } + if let Some(ConstStability { level: Unstable { .. }, feature, .. }) = const_stab { + if ACCEPTED_FEATURES.iter().find(|f| f.name == feature).is_some() { + self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature { + span: const_span.unwrap(), // If const_stab contains Some(..), same is true for const_span + item_sp, + }); + } + } if let Some(ConstStability { level: Unstable { implied_by: Some(implied_by), .. }, feature, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 66c1ff93ce1..b84cbf9c629 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -779,7 +779,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r let prev = self.diag_metadata.current_trait_object; let prev_ty = self.diag_metadata.current_type_path; match &ty.kind { - TyKind::Ref(None, _) => { + TyKind::Ref(None, _) | TyKind::PinnedRef(None, _) => { // Elided lifetime in reference: we resolve as if there was some lifetime `'_` with // NodeId `ty.id`. // This span will be used in case of elision failure. @@ -2326,7 +2326,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { impl<'ra> Visitor<'ra> for FindReferenceVisitor<'_, '_, '_> { fn visit_ty(&mut self, ty: &'ra Ty) { trace!("FindReferenceVisitor considering ty={:?}", ty); - if let TyKind::Ref(lt, _) = ty.kind { + if let TyKind::Ref(lt, _) | TyKind::PinnedRef(lt, _) = ty.kind { // See if anything inside the &thing contains Self let mut visitor = SelfVisitor { r: self.r, impl_self: self.impl_self, self_found: false }; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index fce5ec36c66..50fbdcaf9dc 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -519,11 +519,12 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { continue; }; for bound in bounds { - let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifiers::NONE) = bound - else { + let ast::GenericBound::Trait(trait_ref) = bound else { continue; }; - if base_error.span == trait_ref.span { + if trait_ref.modifiers == ast::TraitBoundModifiers::NONE + && base_error.span == trait_ref.span + { err.span_suggestion_verbose( constraint.ident.span.between(trait_ref.span), "you might have meant to write a path instead of an associated type bound", @@ -837,7 +838,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { ); if bounds.iter().all(|bound| match bound { ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true, - ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, + ast::GenericBound::Trait(tr) => tr.span == base_error.span, }) { let mut sugg = vec![]; if base_error.span != start_span { @@ -1210,7 +1211,8 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let param = generics.params.iter().find_map(|param| { // Only consider type params with exactly one trait bound. if let [bound] = &*param.bounds - && let ast::GenericBound::Trait(tref, ast::TraitBoundModifiers::NONE) = bound + && let ast::GenericBound::Trait(tref) = bound + && tref.modifiers == ast::TraitBoundModifiers::NONE && tref.span == span && param.ident.span.eq_ctxt(span) { @@ -1333,8 +1335,9 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } if let ( [ast::PathSegment { args: None, .. }], - [ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifiers::NONE)], + [ast::GenericBound::Trait(poly_trait_ref)], ) = (&type_param_path.segments[..], &bounds[..]) + && poly_trait_ref.modifiers == ast::TraitBoundModifiers::NONE { if let [ast::PathSegment { ident, args: None, .. }] = &poly_trait_ref.trait_ref.path.segments[..] @@ -2814,7 +2817,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { && bounded_ty.id == binder { for bound in bounds { - if let ast::GenericBound::Trait(poly_trait_ref, _) = bound + if let ast::GenericBound::Trait(poly_trait_ref) = bound && let span = poly_trait_ref .span .with_hi(poly_trait_ref.trait_ref.path.span.lo()) @@ -3233,7 +3236,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut lt_finder = LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] }; for bound in arg_refs { - if let ast::GenericBound::Trait(trait_ref, _) = bound { + if let ast::GenericBound::Trait(trait_ref) = bound { lt_finder.visit_trait_ref(&trait_ref.trait_ref); } } @@ -3444,17 +3447,15 @@ fn mk_where_bound_predicate( span: DUMMY_SP, bound_generic_params: ThinVec::new(), bounded_ty: ast::ptr::P(ty.clone()), - bounds: vec![ast::GenericBound::Trait( - ast::PolyTraitRef { - bound_generic_params: ThinVec::new(), - trait_ref: ast::TraitRef { - path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None }, - ref_id: DUMMY_NODE_ID, - }, - span: DUMMY_SP, + bounds: vec![ast::GenericBound::Trait(ast::PolyTraitRef { + bound_generic_params: ThinVec::new(), + modifiers: ast::TraitBoundModifiers::NONE, + trait_ref: ast::TraitRef { + path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None }, + ref_id: DUMMY_NODE_ID, }, - ast::TraitBoundModifiers::NONE, - )], + span: DUMMY_SP, + })], }; Some(new_where_bound_predicate) @@ -3482,7 +3483,7 @@ struct LifetimeFinder<'ast> { impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> { fn visit_ty(&mut self, t: &'ast Ty) { - if let TyKind::Ref(_, mut_ty) = &t.kind { + if let TyKind::Ref(_, mut_ty) | TyKind::PinnedRef(_, mut_ty) = &t.kind { self.seen.push(t); if t.span.lo() == self.lifetime.lo() { self.found = Some(&mut_ty.ty); diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index b7977a848ce..47f72298e22 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -10,7 +10,6 @@ test(attr(allow(unused_variables), deny(warnings))) )] #![doc(rust_logo)] -#![feature(const_option)] #![feature(core_intrinsics)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 9781c746520..3d44810e7dd 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -66,7 +66,7 @@ impl GatedSpans { #[derive(Default)] pub struct SymbolGallery { /// All symbols occurred and their first occurrence span. - pub symbols: Lock<FxHashMap<Symbol, Span>>, + pub symbols: Lock<FxIndexMap<Symbol, Span>>, } impl SymbolGallery { diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 37528a4425f..9182789cf02 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -137,7 +137,7 @@ pub fn extra_compiler_flags() -> Option<(Vec<String>, bool)> { let content = if arg.len() == a.len() { // A space-separated option, like `-C incremental=foo` or `--crate-type rlib` match args.next() { - Some(arg) => arg.to_string(), + Some(arg) => arg, None => continue, } } else if arg.get(a.len()..a.len() + 1) == Some("=") { diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index c52d1fcc07f..781fe6a11fe 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -19,5 +19,5 @@ scoped-tls = "1.0" sha1 = "0.10.0" sha2 = "0.10.1" tracing = "0.1" -unicode-width = "0.1.4" +unicode-width = "0.2.0" # tidy-alphabetical-end diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cc3bda99a11..6f62b4f82d7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -481,6 +481,8 @@ symbols! { audit_that, augmented_assignments, auto_traits, + autodiff, + autodiff_fallback, automatically_derived, avx, avx512_target_feature, @@ -544,6 +546,7 @@ symbols! { cfg_accessible, cfg_attr, cfg_attr_multi, + cfg_autodiff_fallback, cfg_boolean_literals, cfg_doctest, cfg_eval, @@ -1002,6 +1005,7 @@ symbols! { hashset_iter_ty, hexagon_target_feature, hidden, + hint, homogeneous_aggregate, host, html_favicon_url, @@ -1654,6 +1658,7 @@ symbols! { rustc_allow_incoherent_impl, rustc_allowed_through_unstable_modules, rustc_attrs, + rustc_autodiff, rustc_box, rustc_builtin_macro, rustc_capture_analysis, diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 74970a26b23..b82d327a409 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -64,6 +64,7 @@ impl AArch64InlineAsmRegClass { neon: I8, I16, I32, I64, F16, F32, F64, F128, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF16(4), VecF32(2), VecF64(1), VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2); + // Note: When adding support for SVE vector types, they must be rejected for Arm64EC. }, Self::preg => &[], } @@ -96,7 +97,7 @@ fn restricted_for_arm64ec( _is_clobber: bool, ) -> Result<(), &'static str> { if arch == InlineAsmArch::Arm64EC { - Err("x13, x14, x23, x24, x28, v16-v31 cannot be used for Arm64EC") + Err("x13, x14, x23, x24, x28, v16-v31, p*, ffr cannot be used for Arm64EC") } else { Ok(()) } @@ -165,23 +166,23 @@ def_regs! { v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"] % restricted_for_arm64ec, v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"] % restricted_for_arm64ec, v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31", "z31"] % restricted_for_arm64ec, - p0: preg = ["p0"], - p1: preg = ["p1"], - p2: preg = ["p2"], - p3: preg = ["p3"], - p4: preg = ["p4"], - p5: preg = ["p5"], - p6: preg = ["p6"], - p7: preg = ["p7"], - p8: preg = ["p8"], - p9: preg = ["p9"], - p10: preg = ["p10"], - p11: preg = ["p11"], - p12: preg = ["p12"], - p13: preg = ["p13"], - p14: preg = ["p14"], - p15: preg = ["p15"], - ffr: preg = ["ffr"], + p0: preg = ["p0"] % restricted_for_arm64ec, + p1: preg = ["p1"] % restricted_for_arm64ec, + p2: preg = ["p2"] % restricted_for_arm64ec, + p3: preg = ["p3"] % restricted_for_arm64ec, + p4: preg = ["p4"] % restricted_for_arm64ec, + p5: preg = ["p5"] % restricted_for_arm64ec, + p6: preg = ["p6"] % restricted_for_arm64ec, + p7: preg = ["p7"] % restricted_for_arm64ec, + p8: preg = ["p8"] % restricted_for_arm64ec, + p9: preg = ["p9"] % restricted_for_arm64ec, + p10: preg = ["p10"] % restricted_for_arm64ec, + p11: preg = ["p11"] % restricted_for_arm64ec, + p12: preg = ["p12"] % restricted_for_arm64ec, + p13: preg = ["p13"] % restricted_for_arm64ec, + p14: preg = ["p14"] % restricted_for_arm64ec, + p15: preg = ["p15"] % restricted_for_arm64ec, + ffr: preg = ["ffr"] % restricted_for_arm64ec, #error = ["x19", "w19"] => "x19 is used internally by LLVM and cannot be used as an operand for inline asm", #error = ["x29", "w29", "fp", "wfp"] => @@ -200,12 +201,66 @@ impl AArch64InlineAsmReg { _arch: InlineAsmArch, modifier: Option<char>, ) -> fmt::Result { - let (prefix, index) = if (self as u32) < Self::v0 as u32 { - (modifier.unwrap_or('x'), self as u32 - Self::x0 as u32) + let (prefix, index) = if let Some(index) = self.reg_index() { + (modifier.unwrap_or('x'), index) + } else if let Some(index) = self.vreg_index() { + (modifier.unwrap_or('v'), index) } else { - (modifier.unwrap_or('v'), self as u32 - Self::v0 as u32) + return out.write_str(self.name()); }; assert!(index < 32); write!(out, "{prefix}{index}") } + + /// If the register is an integer register then return its index. + pub fn reg_index(self) -> Option<u32> { + // Unlike `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. + use AArch64InlineAsmReg::*; + Some(match self { + 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, + }) + } + + /// If the register is a vector register then return its index. + pub fn vreg_index(self) -> Option<u32> { + use AArch64InlineAsmReg::*; + if self as u32 >= v0 as u32 && self as u32 <= v31 as u32 { + return Some(self as u32 - v0 as u32); + } + None + } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 73ae1ae96ae..4b539eb8e20 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -890,6 +890,7 @@ pub enum InlineAsmClobberAbi { Arm, AArch64, AArch64NoX18, + Arm64EC, RiscV, LoongArch, S390x, @@ -932,7 +933,7 @@ impl InlineAsmClobberAbi { _ => Err(&["C", "system", "efiapi"]), }, InlineAsmArch::Arm64EC => match name { - "C" | "system" => Ok(InlineAsmClobberAbi::AArch64NoX18), + "C" | "system" => Ok(InlineAsmClobberAbi::Arm64EC), _ => Err(&["C", "system"]), }, InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name { @@ -1033,7 +1034,6 @@ impl InlineAsmClobberAbi { p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, ffr, - } }, InlineAsmClobberAbi::AArch64NoX18 => clobbered_regs! { @@ -1052,7 +1052,20 @@ impl InlineAsmClobberAbi { p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, ffr, + } + }, + InlineAsmClobberAbi::Arm64EC => clobbered_regs! { + AArch64 AArch64InlineAsmReg { + // x13 and x14 cannot be used in Arm64EC. + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x15, + x16, x17, x30, + // Technically the low 64 bits of v8-v15 are preserved, but + // we have no way of expressing this using clobbers. + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, + // v16-v31, p*, and ffr cannot be used in Arm64EC. } }, InlineAsmClobberAbi::Arm => clobbered_regs! { diff --git a/compiler/rustc_target/src/abi/call/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index 55b65fb1caa..55b65fb1caa 100644 --- a/compiler/rustc_target/src/abi/call/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs diff --git a/compiler/rustc_target/src/abi/call/amdgpu.rs b/compiler/rustc_target/src/callconv/amdgpu.rs index 3007a729a8b..3007a729a8b 100644 --- a/compiler/rustc_target/src/abi/call/amdgpu.rs +++ b/compiler/rustc_target/src/callconv/amdgpu.rs diff --git a/compiler/rustc_target/src/abi/call/arm.rs b/compiler/rustc_target/src/callconv/arm.rs index bd6f781fb81..bd6f781fb81 100644 --- a/compiler/rustc_target/src/abi/call/arm.rs +++ b/compiler/rustc_target/src/callconv/arm.rs diff --git a/compiler/rustc_target/src/abi/call/avr.rs b/compiler/rustc_target/src/callconv/avr.rs index dfc991e0954..dfc991e0954 100644 --- a/compiler/rustc_target/src/abi/call/avr.rs +++ b/compiler/rustc_target/src/callconv/avr.rs diff --git a/compiler/rustc_target/src/abi/call/bpf.rs b/compiler/rustc_target/src/callconv/bpf.rs index f19772ac709..f19772ac709 100644 --- a/compiler/rustc_target/src/abi/call/bpf.rs +++ b/compiler/rustc_target/src/callconv/bpf.rs diff --git a/compiler/rustc_target/src/abi/call/csky.rs b/compiler/rustc_target/src/callconv/csky.rs index b1c1ae814a7..b1c1ae814a7 100644 --- a/compiler/rustc_target/src/abi/call/csky.rs +++ b/compiler/rustc_target/src/callconv/csky.rs diff --git a/compiler/rustc_target/src/abi/call/hexagon.rs b/compiler/rustc_target/src/callconv/hexagon.rs index 0a0688880c0..0a0688880c0 100644 --- a/compiler/rustc_target/src/abi/call/hexagon.rs +++ b/compiler/rustc_target/src/callconv/hexagon.rs diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 4a21935623b..4a21935623b 100644 --- a/compiler/rustc_target/src/abi/call/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs diff --git a/compiler/rustc_target/src/abi/call/m68k.rs b/compiler/rustc_target/src/callconv/m68k.rs index 82fe81f8c52..82fe81f8c52 100644 --- a/compiler/rustc_target/src/abi/call/m68k.rs +++ b/compiler/rustc_target/src/callconv/m68k.rs diff --git a/compiler/rustc_target/src/abi/call/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index 37980a91c76..37980a91c76 100644 --- a/compiler/rustc_target/src/abi/call/mips.rs +++ b/compiler/rustc_target/src/callconv/mips.rs diff --git a/compiler/rustc_target/src/abi/call/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 2c3258c8d42..2c3258c8d42 100644 --- a/compiler/rustc_target/src/abi/call/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 352861c5ccb..832246495bc 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -1,10 +1,11 @@ use std::fmt; use std::str::FromStr; +pub use rustc_abi::{Reg, RegKind}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; -use crate::abi::{self, Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; +use crate::abi::{self, Abi, Align, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, WasmCAbi}; mod aarch64; @@ -192,63 +193,6 @@ impl ArgAttributes { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum RegKind { - Integer, - Float, - Vector, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub struct Reg { - pub kind: RegKind, - pub size: Size, -} - -macro_rules! reg_ctor { - ($name:ident, $kind:ident, $bits:expr) => { - pub fn $name() -> Reg { - Reg { kind: RegKind::$kind, size: Size::from_bits($bits) } - } - }; -} - -impl Reg { - reg_ctor!(i8, Integer, 8); - reg_ctor!(i16, Integer, 16); - reg_ctor!(i32, Integer, 32); - reg_ctor!(i64, Integer, 64); - reg_ctor!(i128, Integer, 128); - - reg_ctor!(f32, Float, 32); - reg_ctor!(f64, Float, 64); -} - -impl Reg { - pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { - let dl = cx.data_layout(); - match self.kind { - RegKind::Integer => match self.size.bits() { - 1 => dl.i1_align.abi, - 2..=8 => dl.i8_align.abi, - 9..=16 => dl.i16_align.abi, - 17..=32 => dl.i32_align.abi, - 33..=64 => dl.i64_align.abi, - 65..=128 => dl.i128_align.abi, - _ => panic!("unsupported integer: {self:?}"), - }, - RegKind::Float => match self.size.bits() { - 16 => dl.f16_align.abi, - 32 => dl.f32_align.abi, - 64 => dl.f64_align.abi, - 128 => dl.f128_align.abi, - _ => panic!("unsupported float: {self:?}"), - }, - RegKind::Vector => dl.vector_align(self.size).abi, - } - } -} - /// An argument passed entirely registers with the /// same kind (e.g., HFA / HVA on PPC64 and AArch64). #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)] @@ -380,195 +324,6 @@ impl CastTarget { } } -/// Return value from the `homogeneous_aggregate` test function. -#[derive(Copy, Clone, Debug)] -pub enum HomogeneousAggregate { - /// Yes, all the "leaf fields" of this struct are passed in the - /// same way (specified in the `Reg` value). - Homogeneous(Reg), - - /// There are no leaf fields at all. - NoData, -} - -/// Error from the `homogeneous_aggregate` test function, indicating -/// there are distinct leaf fields passed in different ways, -/// or this is uninhabited. -#[derive(Copy, Clone, Debug)] -pub struct Heterogeneous; - -impl HomogeneousAggregate { - /// If this is a homogeneous aggregate, returns the homogeneous - /// unit, else `None`. - pub fn unit(self) -> Option<Reg> { - match self { - HomogeneousAggregate::Homogeneous(reg) => Some(reg), - HomogeneousAggregate::NoData => None, - } - } - - /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in - /// the same `struct`. Only succeeds if only one of them has any data, - /// or both units are identical. - fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> { - match (self, other) { - (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x), - - (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => { - if a != b { - return Err(Heterogeneous); - } - Ok(self) - } - } - } -} - -impl<'a, Ty> TyAndLayout<'a, Ty> { - /// Returns `true` if this is an aggregate type (including a ScalarPair!) - fn is_aggregate(&self) -> bool { - match self.abi { - Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, - Abi::ScalarPair(..) | Abi::Aggregate { .. } => true, - } - } - - /// Returns `Homogeneous` if this layout is an aggregate containing fields of - /// only a single type (e.g., `(u32, u32)`). Such aggregates are often - /// special-cased in ABIs. - /// - /// Note: We generally ignore 1-ZST fields when computing this value (see #56877). - /// - /// This is public so that it can be used in unit tests, but - /// should generally only be relevant to the ABI details of - /// specific targets. - pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous> - where - Ty: TyAbiInterface<'a, C> + Copy, - { - match self.abi { - Abi::Uninhabited => Err(Heterogeneous), - - // The primitive for this algorithm. - Abi::Scalar(scalar) => { - let kind = match scalar.primitive() { - abi::Int(..) | abi::Pointer(_) => RegKind::Integer, - abi::Float(_) => RegKind::Float, - }; - Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size })) - } - - Abi::Vector { .. } => { - assert!(!self.is_zst()); - Ok(HomogeneousAggregate::Homogeneous(Reg { - kind: RegKind::Vector, - size: self.size, - })) - } - - Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => { - // Helper for computing `homogeneous_aggregate`, allowing a custom - // starting offset (used below for handling variants). - let from_fields_at = - |layout: Self, - start: Size| - -> Result<(HomogeneousAggregate, Size), Heterogeneous> { - let is_union = match layout.fields { - FieldsShape::Primitive => { - unreachable!("aggregates can't have `FieldsShape::Primitive`") - } - FieldsShape::Array { count, .. } => { - assert_eq!(start, Size::ZERO); - - let result = if count > 0 { - layout.field(cx, 0).homogeneous_aggregate(cx)? - } else { - HomogeneousAggregate::NoData - }; - return Ok((result, layout.size)); - } - FieldsShape::Union(_) => true, - FieldsShape::Arbitrary { .. } => false, - }; - - let mut result = HomogeneousAggregate::NoData; - let mut total = start; - - for i in 0..layout.fields.count() { - let field = layout.field(cx, i); - if field.is_1zst() { - // No data here and no impact on layout, can be ignored. - // (We might be able to also ignore all aligned ZST but that's less clear.) - continue; - } - - if !is_union && total != layout.fields.offset(i) { - // This field isn't just after the previous one we considered, abort. - return Err(Heterogeneous); - } - - result = result.merge(field.homogeneous_aggregate(cx)?)?; - - // Keep track of the offset (without padding). - let size = field.size; - if is_union { - total = total.max(size); - } else { - total += size; - } - } - - Ok((result, total)) - }; - - let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?; - - match &self.variants { - abi::Variants::Single { .. } => {} - abi::Variants::Multiple { variants, .. } => { - // Treat enum variants like union members. - // HACK(eddyb) pretend the `enum` field (discriminant) - // is at the start of every variant (otherwise the gap - // at the start of all variants would disqualify them). - // - // NB: for all tagged `enum`s (which include all non-C-like - // `enum`s with defined FFI representation), this will - // match the homogeneous computation on the equivalent - // `struct { tag; union { variant1; ... } }` and/or - // `union { struct { tag; variant1; } ... }` - // (the offsets of variant fields should be identical - // between the two for either to be a homogeneous aggregate). - let variant_start = total; - for variant_idx in variants.indices() { - let (variant_result, variant_total) = - from_fields_at(self.for_variant(cx, variant_idx), variant_start)?; - - result = result.merge(variant_result)?; - total = total.max(variant_total); - } - } - } - - // There needs to be no padding. - if total != self.size { - Err(Heterogeneous) - } else { - match result { - HomogeneousAggregate::Homogeneous(_) => { - assert_ne!(total, Size::ZERO); - } - HomogeneousAggregate::NoData => { - assert_eq!(total, Size::ZERO); - } - } - Ok(result) - } - } - Abi::Aggregate { sized: false } => Err(Heterogeneous), - } - } -} - /// Information about how to pass an argument to, /// or return a value from, a function, under some ABI. #[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)] diff --git a/compiler/rustc_target/src/abi/call/msp430.rs b/compiler/rustc_target/src/callconv/msp430.rs index 4f613aa6c15..4f613aa6c15 100644 --- a/compiler/rustc_target/src/abi/call/msp430.rs +++ b/compiler/rustc_target/src/callconv/msp430.rs diff --git a/compiler/rustc_target/src/abi/call/nvptx64.rs b/compiler/rustc_target/src/callconv/nvptx64.rs index 2e8b16d3a93..2e8b16d3a93 100644 --- a/compiler/rustc_target/src/abi/call/nvptx64.rs +++ b/compiler/rustc_target/src/callconv/nvptx64.rs diff --git a/compiler/rustc_target/src/abi/call/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs index f3b05c48173..f3b05c48173 100644 --- a/compiler/rustc_target/src/abi/call/powerpc.rs +++ b/compiler/rustc_target/src/callconv/powerpc.rs diff --git a/compiler/rustc_target/src/abi/call/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 71e533b8cc5..71e533b8cc5 100644 --- a/compiler/rustc_target/src/abi/call/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index be6bc701b49..be6bc701b49 100644 --- a/compiler/rustc_target/src/abi/call/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs diff --git a/compiler/rustc_target/src/abi/call/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs index 502e7331267..502e7331267 100644 --- a/compiler/rustc_target/src/abi/call/s390x.rs +++ b/compiler/rustc_target/src/callconv/s390x.rs diff --git a/compiler/rustc_target/src/abi/call/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 37980a91c76..37980a91c76 100644 --- a/compiler/rustc_target/src/abi/call/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index 835353f76fc..835353f76fc 100644 --- a/compiler/rustc_target/src/abi/call/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/callconv/wasm.rs index 3c4cd76a754..3c4cd76a754 100644 --- a/compiler/rustc_target/src/abi/call/wasm.rs +++ b/compiler/rustc_target/src/callconv/wasm.rs diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index d9af83d3205..d9af83d3205 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 9910e623ac9..9910e623ac9 100644 --- a/compiler/rustc_target/src/abi/call/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs diff --git a/compiler/rustc_target/src/abi/call/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs index e5a20b248e4..e5a20b248e4 100644 --- a/compiler/rustc_target/src/abi/call/x86_win64.rs +++ b/compiler/rustc_target/src/callconv/x86_win64.rs diff --git a/compiler/rustc_target/src/abi/call/xtensa.rs b/compiler/rustc_target/src/callconv/xtensa.rs index e1728b08a39..e1728b08a39 100644 --- a/compiler/rustc_target/src/abi/call/xtensa.rs +++ b/compiler/rustc_target/src/callconv/xtensa.rs diff --git a/compiler/rustc_target/src/json.rs b/compiler/rustc_target/src/json.rs index 2c367defe7b..b09d8d724ef 100644 --- a/compiler/rustc_target/src/json.rs +++ b/compiler/rustc_target/src/json.rs @@ -134,3 +134,9 @@ impl ToJson for TargetMetadata { }) } } + +impl ToJson for rustc_abi::Endian { + fn to_json(&self) -> Json { + self.as_str().to_json() + } +} diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 2121c4110dd..50679ab8cc8 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -21,8 +21,8 @@ use std::path::{Path, PathBuf}; -pub mod abi; pub mod asm; +pub mod callconv; pub mod json; pub mod spec; pub mod target_features; @@ -30,6 +30,15 @@ pub mod target_features; #[cfg(test)] mod tests; +pub mod abi { + pub(crate) use Float::*; + pub(crate) use Primitive::*; + // Explicitly import `Float` to avoid ambiguity with `Primitive::Float`. + pub use rustc_abi::{Float, *}; + + pub use crate::callconv as call; +} + pub use rustc_abi::HashStableContext; /// The name of rustc's own place to organize libraries. diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs index de8d99977b4..e869314d4d8 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs @@ -1,4 +1,4 @@ -use crate::spec::{CodeModel, Target, TargetOptions, base}; +use crate::spec::{CodeModel, SanitizerSet, Target, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -18,6 +18,11 @@ pub(crate) fn target() -> Target { features: "+f,+d".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::LEAK + | SanitizerSet::MEMORY + | SanitizerSet::THREAD, direct_access_external_data: Some(false), ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs index a5088bcd5c2..70e8bf633a9 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs @@ -1,4 +1,4 @@ -use crate::spec::{CodeModel, Target, TargetOptions, base}; +use crate::spec::{CodeModel, SanitizerSet, Target, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -19,6 +19,11 @@ pub(crate) fn target() -> Target { llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), crt_static_default: false, + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::LEAK + | SanitizerSet::MEMORY + | SanitizerSet::THREAD, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs index 32a856be669..90bcd9a45cf 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs @@ -1,4 +1,4 @@ -use crate::spec::{Target, TargetOptions, base}; +use crate::spec::{SanitizerSet, Target, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -17,6 +17,11 @@ pub(crate) fn target() -> Target { features: "+f,+d".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::LEAK + | SanitizerSet::MEMORY + | SanitizerSet::THREAD, ..base::linux_ohos::opts() }, } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index 7a44c2ad661..2ecd28f4868 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -86,7 +86,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { } hir::TyKind::TraitObject(bounds, ..) => { - for (bound, _) in bounds { + for bound in bounds { self.current_index.shift_in(1); self.visit_poly_trait_ref(bound); self.current_index.shift_out(1); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index a6ecd1cc987..8541621b23b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -599,7 +599,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { _, ) = t.kind { - for (ptr, _) in poly_trait_refs { + for ptr in poly_trait_refs { if Some(self.1) == ptr.trait_ref.trait_def_id() { self.0.push(ptr.span); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index cf0ab630f2e..62204f63dd0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -894,7 +894,9 @@ fn foo(&self) -> Self::T { String::new() } // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. let trait_bounds = bounds.iter().filter_map(|bound| match bound { - hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr), + hir::GenericBound::Trait(ptr) if ptr.modifiers == hir::TraitBoundModifier::None => { + Some(ptr) + } _ => None, }); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index 709b6eb18e3..fc2d0ba36f0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -740,9 +740,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match ( left, right, ) { - (hir::GenericBound::Trait(tl, ml), hir::GenericBound::Trait(tr, mr)) + // FIXME: Suspicious + (hir::GenericBound::Trait(tl), hir::GenericBound::Trait(tr)) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id() - && ml == mr => + && tl.modifiers == tr.modifiers => { true } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index becc1acfb66..ba57909fc23 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -439,7 +439,7 @@ pub fn report_dyn_incompatibility<'tcx>( if tcx.parent_hir_node(hir_id).fn_sig().is_some() { // Do not suggest `impl Trait` when dealing with things like super-traits. err.span_suggestion_verbose( - ty.span.until(trait_ref.0.span), + ty.span.until(trait_ref.span), "consider using an opaque type instead", "impl ", Applicability::MaybeIncorrect, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 87834c329e1..733baaa99e5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3074,11 +3074,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match ty.kind { hir::TyKind::TraitObject(traits, _, _) => { let (span, kw) = match traits { - [(first, _), ..] if first.span.lo() == ty.span.lo() => { + [first, ..] if first.span.lo() == ty.span.lo() => { // Missing `dyn` in front of trait object. (ty.span.shrink_to_lo(), "dyn ") } - [(first, _), ..] => (ty.span.until(first.span), ""), + [first, ..] => (ty.span.until(first.span), ""), [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"), }; let needs_parens = traits.len() != 1; @@ -5162,7 +5162,7 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( let async_span = tcx.sess.source_map().span_extend_while_whitespace(async_span); let future = tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty(); - let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else { + let [hir::GenericBound::Trait(trait_ref)] = future.bounds else { // `async fn` should always lower to a single bound... but don't ICE. return None; }; diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 45e7de942fb..364a13b3a75 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -125,7 +125,7 @@ fn sized_trait_bound_spans<'tcx>( bounds: hir::GenericBounds<'tcx>, ) -> impl 'tcx + Iterator<Item = Span> { bounds.iter().filter_map(move |b| match b { - hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None) + hir::GenericBound::Trait(trait_ref) if trait_has_sized_self( tcx, trait_ref.trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index f86face3f90..dbfd2e74abe 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -225,7 +225,6 @@ impl<B: ?Sized + ToOwned> Cow<'_, B> { /// assert!(!bull.is_borrowed()); /// ``` #[unstable(feature = "cow_is_borrowed", issue = "65143")] - #[rustc_const_unstable(feature = "const_cow_is_borrowed", issue = "65143")] pub const fn is_borrowed(&self) -> bool { match *self { Borrowed(_) => true, @@ -248,7 +247,6 @@ impl<B: ?Sized + ToOwned> Cow<'_, B> { /// assert!(!bull.is_owned()); /// ``` #[unstable(feature = "cow_is_borrowed", issue = "65143")] - #[rustc_const_unstable(feature = "const_cow_is_borrowed", issue = "65143")] pub const fn is_owned(&self) -> bool { !self.is_borrowed() } diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index b1759a486ac..54739c50d1d 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -103,6 +103,7 @@ pub struct VecDeque< #[stable(feature = "rust1", since = "1.0.0")] impl<T: Clone, A: Allocator + Clone> Clone for VecDeque<T, A> { + #[track_caller] fn clone(&self) -> Self { let mut deq = Self::with_capacity_in(self.len(), self.allocator().clone()); deq.extend(self.iter().cloned()); @@ -113,6 +114,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for VecDeque<T, A> { /// /// This method is preferred over simply assigning `source.clone()` to `self`, /// as it avoids reallocation if possible. + #[track_caller] fn clone_from(&mut self, source: &Self) { self.clear(); self.extend(source.iter().cloned()); @@ -570,6 +572,7 @@ impl<T> VecDeque<T> { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] + #[track_caller] pub fn with_capacity(capacity: usize) -> VecDeque<T> { Self::with_capacity_in(capacity, Global) } @@ -625,6 +628,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// let deque: VecDeque<u32> = VecDeque::with_capacity(10); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] + #[track_caller] pub fn with_capacity_in(capacity: usize, alloc: A) -> VecDeque<T, A> { VecDeque { head: 0, len: 0, buf: RawVec::with_capacity_in(capacity, alloc) } } @@ -789,6 +793,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// /// [`reserve`]: VecDeque::reserve #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] pub fn reserve_exact(&mut self, additional: usize) { let new_cap = self.len.checked_add(additional).expect("capacity overflow"); let old_cap = self.capacity(); @@ -818,6 +823,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// assert!(buf.capacity() >= 11); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] pub fn reserve(&mut self, additional: usize) { let new_cap = self.len.checked_add(additional).expect("capacity overflow"); let old_cap = self.capacity(); @@ -949,6 +955,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// assert!(buf.capacity() >= 4); /// ``` #[stable(feature = "deque_extras_15", since = "1.5.0")] + #[track_caller] pub fn shrink_to_fit(&mut self) { self.shrink_to(0); } @@ -974,6 +981,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// assert!(buf.capacity() >= 4); /// ``` #[stable(feature = "shrink_to", since = "1.56.0")] + #[track_caller] pub fn shrink_to(&mut self, min_capacity: usize) { let target_cap = min_capacity.max(self.len); @@ -1740,6 +1748,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// assert_eq!(d.front(), Some(&2)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] pub fn push_front(&mut self, value: T) { if self.is_full() { self.grow(); @@ -1767,6 +1776,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push", "put", "append")] + #[track_caller] pub fn push_back(&mut self, value: T) { if self.is_full() { self.grow(); @@ -1876,6 +1886,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// assert_eq!(vec_deque, &['a', 'd', 'b', 'c']); /// ``` #[stable(feature = "deque_extras_15", since = "1.5.0")] + #[track_caller] pub fn insert(&mut self, index: usize, value: T) { assert!(index <= self.len(), "index out of bounds"); if self.is_full() { @@ -1979,6 +1990,7 @@ impl<T, A: Allocator> VecDeque<T, A> { #[inline] #[must_use = "use `.truncate()` if you don't need the other half"] #[stable(feature = "split_off", since = "1.4.0")] + #[track_caller] pub fn split_off(&mut self, at: usize) -> Self where A: Clone, @@ -2045,6 +2057,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` #[inline] #[stable(feature = "append", since = "1.4.0")] + #[track_caller] pub fn append(&mut self, other: &mut Self) { if T::IS_ZST { self.len = self.len.checked_add(other.len).expect("capacity overflow"); @@ -2167,6 +2180,7 @@ impl<T, A: Allocator> VecDeque<T, A> { // be called in cold paths. // This may panic or abort #[inline(never)] + #[track_caller] fn grow(&mut self) { // Extend or possibly remove this assertion when valid use-cases for growing the // buffer without it being full emerge @@ -2205,6 +2219,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// assert_eq!(buf, [5, 10, 101, 102, 103]); /// ``` #[stable(feature = "vec_resize_with", since = "1.33.0")] + #[track_caller] pub fn resize_with(&mut self, new_len: usize, generator: impl FnMut() -> T) { let len = self.len; @@ -2751,6 +2766,7 @@ impl<T: Clone, A: Allocator> VecDeque<T, A> { /// assert_eq!(buf, [5, 10, 20, 20, 20]); /// ``` #[stable(feature = "deque_extras", since = "1.16.0")] + #[track_caller] pub fn resize(&mut self, new_len: usize, value: T) { if new_len > self.len() { let extra = new_len - self.len(); @@ -2870,6 +2886,7 @@ impl<T, A: Allocator> IndexMut<usize> for VecDeque<T, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> FromIterator<T> for VecDeque<T> { + #[track_caller] fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> VecDeque<T> { SpecFromIter::spec_from_iter(iter.into_iter()) } @@ -2909,16 +2926,19 @@ impl<'a, T, A: Allocator> IntoIterator for &'a mut VecDeque<T, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<T, A: Allocator> Extend<T> for VecDeque<T, A> { + #[track_caller] fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { <Self as SpecExtend<T, I::IntoIter>>::spec_extend(self, iter.into_iter()); } #[inline] + #[track_caller] fn extend_one(&mut self, elem: T) { self.push_back(elem); } #[inline] + #[track_caller] fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } @@ -2934,16 +2954,19 @@ impl<T, A: Allocator> Extend<T> for VecDeque<T, A> { #[stable(feature = "extend_ref", since = "1.2.0")] impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for VecDeque<T, A> { + #[track_caller] fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) { self.spec_extend(iter.into_iter()); } #[inline] + #[track_caller] fn extend_one(&mut self, &elem: &'a T) { self.push_back(elem); } #[inline] + #[track_caller] fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } @@ -3041,6 +3064,7 @@ impl<T, const N: usize> From<[T; N]> for VecDeque<T> { /// let deq2: VecDeque<_> = [1, 2, 3, 4].into(); /// assert_eq!(deq1, deq2); /// ``` + #[track_caller] fn from(arr: [T; N]) -> Self { let mut deq = VecDeque::with_capacity(N); let arr = ManuallyDrop::new(arr); diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index a9b0fd073b5..d246385ca84 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -7,6 +7,7 @@ use crate::vec; // Specialization trait used for VecDeque::extend pub(super) trait SpecExtend<T, I> { + #[track_caller] fn spec_extend(&mut self, iter: I); } @@ -14,6 +15,7 @@ impl<T, I, A: Allocator> SpecExtend<T, I> for VecDeque<T, A> where I: Iterator<Item = T>, { + #[track_caller] default fn spec_extend(&mut self, mut iter: I) { // This function should be the moral equivalent of: // @@ -44,6 +46,7 @@ impl<T, I, A: Allocator> SpecExtend<T, I> for VecDeque<T, A> where I: TrustedLen<Item = T>, { + #[track_caller] default fn spec_extend(&mut self, iter: I) { // This is the case for a TrustedLen iterator. let (low, high) = iter.size_hint(); @@ -76,6 +79,7 @@ where } impl<T, A: Allocator> SpecExtend<T, vec::IntoIter<T>> for VecDeque<T, A> { + #[track_caller] fn spec_extend(&mut self, mut iterator: vec::IntoIter<T>) { let slice = iterator.as_slice(); self.reserve(slice.len()); @@ -93,6 +97,7 @@ where I: Iterator<Item = &'a T>, T: Copy, { + #[track_caller] default fn spec_extend(&mut self, iterator: I) { self.spec_extend(iterator.copied()) } @@ -102,6 +107,7 @@ impl<'a, T: 'a, A: Allocator> SpecExtend<&'a T, slice::Iter<'a, T>> for VecDeque where T: Copy, { + #[track_caller] fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { let slice = iterator.as_slice(); self.reserve(slice.len()); diff --git a/library/alloc/src/collections/vec_deque/spec_from_iter.rs b/library/alloc/src/collections/vec_deque/spec_from_iter.rs index 2708c7fe102..1efe84d6d7d 100644 --- a/library/alloc/src/collections/vec_deque/spec_from_iter.rs +++ b/library/alloc/src/collections/vec_deque/spec_from_iter.rs @@ -9,6 +9,7 @@ impl<T, I> SpecFromIter<T, I> for VecDeque<T> where I: Iterator<Item = T>, { + #[track_caller] default fn spec_from_iter(iterator: I) -> Self { // Since converting is O(1) now, just re-use the `Vec` logic for // anything where we can't do something extra-special for `VecDeque`, diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index d496899e72b..d7e99f4a1a6 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -7,7 +7,7 @@ use core::borrow::Borrow; use core::ffi::{CStr, c_char}; use core::num::NonZero; use core::slice::memchr; -use core::str::{self, Utf8Error}; +use core::str::{self, FromStr, Utf8Error}; use core::{fmt, mem, ops, ptr, slice}; use crate::borrow::{Cow, ToOwned}; @@ -817,6 +817,30 @@ impl From<Vec<NonZero<u8>>> for CString { } } +impl FromStr for CString { + type Err = NulError; + + /// Converts a string `s` into a [`CString`]. + /// + /// This method is equivalent to [`CString::new`]. + #[inline] + fn from_str(s: &str) -> Result<Self, Self::Err> { + Self::new(s) + } +} + +impl TryFrom<CString> for String { + type Error = IntoStringError; + + /// Converts a [`CString`] into a [`String`] if it contains valid UTF-8 data. + /// + /// This method is equivalent to [`CString::into_string`]. + #[inline] + fn try_from(value: CString) -> Result<Self, Self::Error> { + value.into_string() + } +} + #[cfg(not(test))] #[stable(feature = "more_box_slice_clone", since = "1.29.0")] impl Clone for Box<CStr> { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index c60c0743c7e..50bf385d671 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -108,11 +108,9 @@ #![feature(coerce_unsized)] #![feature(const_align_of_val)] #![feature(const_box)] -#![feature(const_cow_is_borrowed)] #![feature(const_eval_select)] #![feature(const_heap)] #![feature(const_maybe_uninit_write)] -#![feature(const_option)] #![feature(const_pin)] #![feature(const_size_of_val)] #![feature(const_vec_string_slice)] @@ -173,7 +171,6 @@ #![feature(allow_internal_unstable)] #![feature(cfg_sanitize)] #![feature(const_precise_live_drops)] -#![feature(const_ptr_write)] #![feature(const_try)] #![feature(decl_macro)] #![feature(dropck_eyepatch)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 8fdca8c4200..45de0617f33 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -20,6 +20,7 @@ mod tests; // only one location which panics rather than a bunch throughout the module. #[cfg(not(no_global_oom_handling))] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[track_caller] fn capacity_overflow() -> ! { panic!("capacity overflow"); } @@ -125,6 +126,7 @@ impl<T> RawVec<T, Global> { #[cfg(not(any(no_global_oom_handling, test)))] #[must_use] #[inline] + #[track_caller] pub fn with_capacity(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } @@ -133,6 +135,7 @@ impl<T> RawVec<T, Global> { #[cfg(not(any(no_global_oom_handling, test)))] #[must_use] #[inline] + #[track_caller] pub fn with_capacity_zeroed(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), @@ -145,6 +148,7 @@ impl RawVecInner<Global> { #[cfg(not(any(no_global_oom_handling, test)))] #[must_use] #[inline] + #[track_caller] fn with_capacity(capacity: usize, elem_layout: Layout) -> Self { match Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global, elem_layout) { Ok(res) => res, @@ -184,6 +188,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// allocator for the returned `RawVec`. #[cfg(not(no_global_oom_handling))] #[inline] + #[track_caller] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), @@ -205,6 +210,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// of allocator for the returned `RawVec`. #[cfg(not(no_global_oom_handling))] #[inline] + #[track_caller] pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), @@ -324,6 +330,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] #[inline] + #[track_caller] pub fn reserve(&mut self, len: usize, additional: usize) { self.inner.reserve(len, additional, T::LAYOUT) } @@ -332,6 +339,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// caller to ensure `len == self.capacity()`. #[cfg(not(no_global_oom_handling))] #[inline(never)] + #[track_caller] pub fn grow_one(&mut self) { self.inner.grow_one(T::LAYOUT) } @@ -359,6 +367,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] + #[track_caller] pub fn reserve_exact(&mut self, len: usize, additional: usize) { self.inner.reserve_exact(len, additional, T::LAYOUT) } @@ -383,6 +392,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] + #[track_caller] #[inline] pub fn shrink_to_fit(&mut self, cap: usize) { self.inner.shrink_to_fit(cap, T::LAYOUT) @@ -408,6 +418,7 @@ impl<A: Allocator> RawVecInner<A> { #[cfg(not(no_global_oom_handling))] #[inline] + #[track_caller] fn with_capacity_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) { Ok(this) => { @@ -432,6 +443,7 @@ impl<A: Allocator> RawVecInner<A> { #[cfg(not(no_global_oom_handling))] #[inline] + #[track_caller] fn with_capacity_zeroed_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc, elem_layout) { Ok(res) => res, @@ -526,6 +538,7 @@ impl<A: Allocator> RawVecInner<A> { #[cfg(not(no_global_oom_handling))] #[inline] + #[track_caller] fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) { // Callers expect this function to be very cheap when there is already sufficient capacity. // Therefore, we move all the resizing and error-handling logic from grow_amortized and @@ -550,6 +563,7 @@ impl<A: Allocator> RawVecInner<A> { #[cfg(not(no_global_oom_handling))] #[inline] + #[track_caller] fn grow_one(&mut self, elem_layout: Layout) { if let Err(err) = self.grow_amortized(self.cap.0, 1, elem_layout) { handle_error(err); @@ -573,6 +587,7 @@ impl<A: Allocator> RawVecInner<A> { } #[cfg(not(no_global_oom_handling))] + #[track_caller] fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) { if let Err(err) = self.try_reserve_exact(len, additional, elem_layout) { handle_error(err); @@ -597,6 +612,7 @@ impl<A: Allocator> RawVecInner<A> { #[cfg(not(no_global_oom_handling))] #[inline] + #[track_caller] fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { if let Err(err) = self.shrink(cap, elem_layout) { handle_error(err); @@ -770,6 +786,7 @@ where #[cfg(not(no_global_oom_handling))] #[cold] #[optimize(size)] +#[track_caller] fn handle_error(e: TryReserveError) -> ! { match e.kind() { CapacityOverflow => capacity_overflow(), diff --git a/library/alloc/src/vec/cow.rs b/library/alloc/src/vec/cow.rs index c18091705a6..4deb35efffc 100644 --- a/library/alloc/src/vec/cow.rs +++ b/library/alloc/src/vec/cow.rs @@ -58,6 +58,7 @@ impl<'a, T> FromIterator<T> for Cow<'a, [T]> where T: Clone, { + #[track_caller] fn from_iter<I: IntoIterator<Item = T>>(it: I) -> Cow<'a, [T]> { Cow::Owned(FromIterator::from_iter(it)) } diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index fd94bbbdeb1..a7dba16944e 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -229,6 +229,7 @@ where I: Iterator<Item = T> + InPlaceCollect, <I as SourceIter>::Source: AsVecIntoIter, { + #[track_caller] default fn from_iter(iterator: I) -> Self { // Select the implementation in const eval to avoid codegen of the dead branch to improve compile times. let fun: fn(I) -> Vec<T> = const { @@ -246,6 +247,7 @@ where } } +#[track_caller] fn from_iter_in_place<I, T>(mut iterator: I) -> Vec<T> where I: Iterator<Item = T> + InPlaceCollect, diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 830512ceee8..07a1bd49321 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -478,6 +478,7 @@ impl<T> Vec<T> { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[cfg_attr(not(test), rustc_diagnostic_item = "vec_with_capacity")] + #[track_caller] pub fn with_capacity(capacity: usize) -> Self { Self::with_capacity_in(capacity, Global) } @@ -797,6 +798,7 @@ impl<T, A: Allocator> Vec<T, A> { #[cfg(not(no_global_oom_handling))] #[inline] #[unstable(feature = "allocator_api", issue = "32838")] + #[track_caller] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 } } @@ -1264,6 +1266,7 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); } @@ -1294,6 +1297,7 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] pub fn reserve_exact(&mut self, additional: usize) { self.buf.reserve_exact(self.len, additional); } @@ -1397,6 +1401,7 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] #[inline] pub fn shrink_to_fit(&mut self) { // The capacity is never less than the length, and there's nothing to do when @@ -1427,6 +1432,7 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "shrink_to", since = "1.56.0")] + #[track_caller] pub fn shrink_to(&mut self, min_capacity: usize) { if self.capacity() > min_capacity { self.buf.shrink_to_fit(cmp::max(self.len, min_capacity)); @@ -1460,6 +1466,7 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] pub fn into_boxed_slice(mut self) -> Box<[T], A> { unsafe { self.shrink_to_fit(); @@ -1958,6 +1965,7 @@ impl<T, A: Allocator> Vec<T, A> { /// the insertion index is 0. #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] pub fn insert(&mut self, index: usize, element: T) { #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] @@ -2397,6 +2405,7 @@ impl<T, A: Allocator> Vec<T, A> { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push_back", "put", "append")] + #[track_caller] pub fn push(&mut self, value: T) { // Inform codegen that the length does not change across grow_one(). let len = self.len; @@ -2538,6 +2547,7 @@ impl<T, A: Allocator> Vec<T, A> { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "append", since = "1.4.0")] + #[track_caller] pub fn append(&mut self, other: &mut Self) { unsafe { self.append_elements(other.as_slice() as _); @@ -2548,6 +2558,7 @@ impl<T, A: Allocator> Vec<T, A> { /// Appends elements to `self` from other buffer. #[cfg(not(no_global_oom_handling))] #[inline] + #[track_caller] unsafe fn append_elements(&mut self, other: *const [T]) { let count = unsafe { (*other).len() }; self.reserve(count); @@ -2711,6 +2722,7 @@ impl<T, A: Allocator> Vec<T, A> { #[inline] #[must_use = "use `.truncate()` if you don't need the other half"] #[stable(feature = "split_off", since = "1.4.0")] + #[track_caller] pub fn split_off(&mut self, at: usize) -> Self where A: Clone, @@ -2768,6 +2780,7 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_resize_with", since = "1.33.0")] + #[track_caller] pub fn resize_with<F>(&mut self, new_len: usize, f: F) where F: FnMut() -> T, @@ -2973,6 +2986,7 @@ impl<T: Clone, A: Allocator> Vec<T, A> { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_resize", since = "1.5.0")] + #[track_caller] pub fn resize(&mut self, new_len: usize, value: T) { let len = self.len(); @@ -3004,6 +3018,7 @@ impl<T: Clone, A: Allocator> Vec<T, A> { /// [`extend`]: Vec::extend #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_extend_from_slice", since = "1.6.0")] + #[track_caller] pub fn extend_from_slice(&mut self, other: &[T]) { self.spec_extend(other.iter()) } @@ -3031,6 +3046,7 @@ impl<T: Clone, A: Allocator> Vec<T, A> { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_extend_from_within", since = "1.53.0")] + #[track_caller] pub fn extend_from_within<R>(&mut self, src: R) where R: RangeBounds<usize>, @@ -3091,6 +3107,7 @@ impl<T, A: Allocator, const N: usize> Vec<[T; N], A> { impl<T: Clone, A: Allocator> Vec<T, A> { #[cfg(not(no_global_oom_handling))] + #[track_caller] /// Extend the vector by `n` clones of value. fn extend_with(&mut self, n: usize, value: T) { self.reserve(n); @@ -3151,6 +3168,7 @@ impl<T: PartialEq, A: Allocator> Vec<T, A> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "vec_from_elem")] +#[track_caller] pub fn from_elem<T: Clone>(elem: T, n: usize) -> Vec<T> { <T as SpecFromElem>::from_elem(elem, n, Global) } @@ -3158,6 +3176,7 @@ pub fn from_elem<T: Clone>(elem: T, n: usize) -> Vec<T> { #[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] +#[track_caller] pub fn from_elem_in<T: Clone, A: Allocator>(elem: T, n: usize, alloc: A) -> Vec<T, A> { <T as SpecFromElem>::from_elem(elem, n, alloc) } @@ -3249,6 +3268,7 @@ unsafe impl<T, A: Allocator> ops::DerefPure for Vec<T, A> {} #[stable(feature = "rust1", since = "1.0.0")] impl<T: Clone, A: Allocator + Clone> Clone for Vec<T, A> { #[cfg(not(test))] + #[track_caller] fn clone(&self) -> Self { let alloc = self.allocator().clone(); <[T]>::to_vec_in(&**self, alloc) @@ -3286,6 +3306,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for Vec<T, A> { /// // And no reallocation occurred /// assert_eq!(yp, y.as_ptr()); /// ``` + #[track_caller] fn clone_from(&mut self, source: &Self) { crate::slice::SpecCloneIntoVec::clone_into(source.as_slice(), self); } @@ -3384,6 +3405,7 @@ impl<T, I: SliceIndex<[T]>, A: Allocator> IndexMut<I> for Vec<T, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> FromIterator<T> for Vec<T> { #[inline] + #[track_caller] fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Vec<T> { <Self as SpecFromIter<T, I::IntoIter>>::from_iter(iter.into_iter()) } @@ -3452,16 +3474,19 @@ impl<'a, T, A: Allocator> IntoIterator for &'a mut Vec<T, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<T, A: Allocator> Extend<T> for Vec<T, A> { #[inline] + #[track_caller] fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { <Self as SpecExtend<T, I::IntoIter>>::spec_extend(self, iter.into_iter()) } #[inline] + #[track_caller] fn extend_one(&mut self, item: T) { self.push(item); } #[inline] + #[track_caller] fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } @@ -3481,6 +3506,7 @@ impl<T, A: Allocator> Vec<T, A> { // leaf method to which various SpecFrom/SpecExtend implementations delegate when // they have no further optimizations to apply #[cfg(not(no_global_oom_handling))] + #[track_caller] fn extend_desugared<I: Iterator<Item = T>>(&mut self, mut iterator: I) { // This is the case for a general iterator. // @@ -3508,6 +3534,7 @@ impl<T, A: Allocator> Vec<T, A> { // specific extend for `TrustedLen` iterators, called both by the specializations // and internal places where resolving specialization makes compilation slower #[cfg(not(no_global_oom_handling))] + #[track_caller] fn extend_trusted(&mut self, iterator: impl iter::TrustedLen<Item = T>) { let (low, high) = iterator.size_hint(); if let Some(additional) = high { @@ -3658,16 +3685,19 @@ impl<T, A: Allocator> Vec<T, A> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "extend_ref", since = "1.2.0")] impl<'a, T: Copy + 'a, A: Allocator> Extend<&'a T> for Vec<T, A> { + #[track_caller] fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) { self.spec_extend(iter.into_iter()) } #[inline] + #[track_caller] fn extend_one(&mut self, &item: &'a T) { self.push(item); } #[inline] + #[track_caller] fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } @@ -3778,6 +3808,7 @@ impl<T: Clone> From<&[T]> for Vec<T> { /// assert_eq!(Vec::from(&[1, 2, 3][..]), vec![1, 2, 3]); /// ``` #[cfg(not(test))] + #[track_caller] fn from(s: &[T]) -> Vec<T> { s.to_vec() } @@ -3798,6 +3829,7 @@ impl<T: Clone> From<&mut [T]> for Vec<T> { /// assert_eq!(Vec::from(&mut [1, 2, 3][..]), vec![1, 2, 3]); /// ``` #[cfg(not(test))] + #[track_caller] fn from(s: &mut [T]) -> Vec<T> { s.to_vec() } @@ -3817,6 +3849,7 @@ impl<T: Clone, const N: usize> From<&[T; N]> for Vec<T> { /// ``` /// assert_eq!(Vec::from(&[1, 2, 3]), vec![1, 2, 3]); /// ``` + #[track_caller] fn from(s: &[T; N]) -> Vec<T> { Self::from(s.as_slice()) } @@ -3832,6 +3865,7 @@ impl<T: Clone, const N: usize> From<&mut [T; N]> for Vec<T> { /// ``` /// assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]); /// ``` + #[track_caller] fn from(s: &mut [T; N]) -> Vec<T> { Self::from(s.as_mut_slice()) } @@ -3848,6 +3882,7 @@ impl<T, const N: usize> From<[T; N]> for Vec<T> { /// assert_eq!(Vec::from([1, 2, 3]), vec![1, 2, 3]); /// ``` #[cfg(not(test))] + #[track_caller] fn from(s: [T; N]) -> Vec<T> { <[T]>::into_vec(Box::new(s)) } @@ -3877,6 +3912,7 @@ where /// let b: Cow<'_, [i32]> = Cow::Borrowed(&[1, 2, 3]); /// assert_eq!(Vec::from(o), Vec::from(b)); /// ``` + #[track_caller] fn from(s: Cow<'a, [T]>) -> Vec<T> { s.into_owned() } @@ -3925,6 +3961,7 @@ impl<T, A: Allocator> From<Vec<T, A>> for Box<[T], A> { /// /// assert_eq!(Box::from(vec), vec![1, 2, 3].into_boxed_slice()); /// ``` + #[track_caller] fn from(v: Vec<T, A>) -> Self { v.into_boxed_slice() } @@ -3940,6 +3977,7 @@ impl From<&str> for Vec<u8> { /// ``` /// assert_eq!(Vec::from("123"), vec![b'1', b'2', b'3']); /// ``` + #[track_caller] fn from(s: &str) -> Vec<u8> { From::from(s.as_bytes()) } diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index 7085bceef5b..b98db669059 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -6,6 +6,7 @@ use crate::alloc::Allocator; // Specialization trait used for Vec::extend pub(super) trait SpecExtend<T, I> { + #[track_caller] fn spec_extend(&mut self, iter: I); } @@ -13,6 +14,7 @@ impl<T, I, A: Allocator> SpecExtend<T, I> for Vec<T, A> where I: Iterator<Item = T>, { + #[track_caller] default fn spec_extend(&mut self, iter: I) { self.extend_desugared(iter) } @@ -22,12 +24,14 @@ impl<T, I, A: Allocator> SpecExtend<T, I> for Vec<T, A> where I: TrustedLen<Item = T>, { + #[track_caller] default fn spec_extend(&mut self, iterator: I) { self.extend_trusted(iterator) } } impl<T, A: Allocator> SpecExtend<T, IntoIter<T>> for Vec<T, A> { + #[track_caller] fn spec_extend(&mut self, mut iterator: IntoIter<T>) { unsafe { self.append_elements(iterator.as_slice() as _); @@ -41,6 +45,7 @@ where I: Iterator<Item = &'a T>, T: Clone, { + #[track_caller] default fn spec_extend(&mut self, iterator: I) { self.spec_extend(iterator.cloned()) } @@ -50,6 +55,7 @@ impl<'a, T: 'a, A: Allocator> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec<T, A where T: Copy, { + #[track_caller] fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { let slice = iterator.as_slice(); unsafe { self.append_elements(slice) }; diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs index 96d701e15d4..6c7b4d89f2d 100644 --- a/library/alloc/src/vec/spec_from_elem.rs +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -10,6 +10,7 @@ pub(super) trait SpecFromElem: Sized { } impl<T: Clone> SpecFromElem for T { + #[track_caller] default fn from_elem<A: Allocator>(elem: Self, n: usize, alloc: A) -> Vec<Self, A> { let mut v = Vec::with_capacity_in(n, alloc); v.extend_with(n, elem); @@ -19,6 +20,7 @@ impl<T: Clone> SpecFromElem for T { impl<T: Clone + IsZero> SpecFromElem for T { #[inline] + #[track_caller] default fn from_elem<A: Allocator>(elem: T, n: usize, alloc: A) -> Vec<T, A> { if elem.is_zero() { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; @@ -31,6 +33,7 @@ impl<T: Clone + IsZero> SpecFromElem for T { impl SpecFromElem for i8 { #[inline] + #[track_caller] fn from_elem<A: Allocator>(elem: i8, n: usize, alloc: A) -> Vec<i8, A> { if elem == 0 { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; @@ -46,6 +49,7 @@ impl SpecFromElem for i8 { impl SpecFromElem for u8 { #[inline] + #[track_caller] fn from_elem<A: Allocator>(elem: u8, n: usize, alloc: A) -> Vec<u8, A> { if elem == 0 { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs index e1f0b639bdf..ad7688e1c59 100644 --- a/library/alloc/src/vec/spec_from_iter.rs +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -29,12 +29,14 @@ impl<T, I> SpecFromIter<T, I> for Vec<T> where I: Iterator<Item = T>, { + #[track_caller] default fn from_iter(iterator: I) -> Self { SpecFromIterNested::from_iter(iterator) } } impl<T> SpecFromIter<T, IntoIter<T>> for Vec<T> { + #[track_caller] fn from_iter(iterator: IntoIter<T>) -> Self { // A common case is passing a vector into a function which immediately // re-collects into a vector. We can short circuit this if the IntoIter diff --git a/library/alloc/src/vec/spec_from_iter_nested.rs b/library/alloc/src/vec/spec_from_iter_nested.rs index 77f7761d22f..22eed238798 100644 --- a/library/alloc/src/vec/spec_from_iter_nested.rs +++ b/library/alloc/src/vec/spec_from_iter_nested.rs @@ -15,6 +15,7 @@ impl<T, I> SpecFromIterNested<T, I> for Vec<T> where I: Iterator<Item = T>, { + #[track_caller] default fn from_iter(mut iterator: I) -> Self { // Unroll the first iteration, as the vector is going to be // expanded on this iteration in every case when the iterable is not @@ -47,6 +48,7 @@ impl<T, I> SpecFromIterNested<T, I> for Vec<T> where I: TrustedLen<Item = T>, { + #[track_caller] fn from_iter(iterator: I) -> Self { let mut vector = match iterator.size_hint() { (_, Some(upper)) => Vec::with_capacity(upper), diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index 9e36377c148..ca5cb17f8bf 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -52,6 +52,7 @@ impl<I: Iterator, A: Allocator> ExactSizeIterator for Splice<'_, I, A> {} #[stable(feature = "vec_splice", since = "1.21.0")] impl<I: Iterator, A: Allocator> Drop for Splice<'_, I, A> { + #[track_caller] fn drop(&mut self) { self.drain.by_ref().for_each(drop); // At this point draining is done and the only remaining tasks are splicing @@ -123,6 +124,7 @@ impl<T, A: Allocator> Drain<'_, T, A> { } /// Makes room for inserting more elements before the tail. + #[track_caller] unsafe fn move_tail(&mut self, additional: usize) { let vec = unsafe { self.vec.as_mut() }; let len = self.tail_start + self.tail_len; diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index f43143c6cef..3ec4332c71b 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -4,10 +4,8 @@ #![feature(assert_matches)] #![feature(btree_extract_if)] #![feature(cow_is_borrowed)] -#![feature(const_cow_is_borrowed)] #![feature(const_heap)] #![cfg_attr(bootstrap, feature(const_mut_refs))] -#![feature(const_ptr_write)] #![feature(const_try)] #![feature(core_intrinsics)] #![feature(extract_if)] diff --git a/library/core/src/array/ascii.rs b/library/core/src/array/ascii.rs index 05797b042ee..e2faef855bc 100644 --- a/library/core/src/array/ascii.rs +++ b/library/core/src/array/ascii.rs @@ -9,7 +9,6 @@ impl<const N: usize> [u8; N] { /// /// ``` /// #![feature(ascii_char)] - /// #![feature(const_option)] /// /// const HEX_DIGITS: [std::ascii::Char; 16] = /// *b"0123456789abcdef".as_ascii().unwrap(); diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index ce09a0b444d..48de4f17b1b 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -506,7 +506,7 @@ impl AsciiChar { pub const unsafe fn digit_unchecked(d: u8) -> Self { assert_unsafe_precondition!( check_language_ub, - "`AsciiChar::digit_unchecked` input cannot exceed 9.", + "`ascii::Char::digit_unchecked` input cannot exceed 9.", (d: u8 = d) => d < 10 ); diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 6bedb0d0dc4..04698193bba 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1282,8 +1282,9 @@ impl char { /// /// [`to_ascii_uppercase()`]: #method.to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); } @@ -1308,8 +1309,9 @@ impl char { /// /// [`to_ascii_lowercase()`]: #method.to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); } diff --git a/library/core/src/escape.rs b/library/core/src/escape.rs index b213cc2b916..0685f525dca 100644 --- a/library/core/src/escape.rs +++ b/library/core/src/escape.rs @@ -18,38 +18,106 @@ const fn backslash<const N: usize>(a: ascii::Char) -> ([ascii::Char; N], Range<u (output, 0..2) } +#[inline] +const fn hex_escape<const N: usize>(byte: u8) -> ([ascii::Char; N], Range<u8>) { + const { assert!(N >= 4) }; + + let mut output = [ascii::Char::Null; N]; + + let hi = HEX_DIGITS[(byte >> 4) as usize]; + let lo = HEX_DIGITS[(byte & 0xf) as usize]; + + output[0] = ascii::Char::ReverseSolidus; + output[1] = ascii::Char::SmallX; + output[2] = hi; + output[3] = lo; + + (output, 0..4) +} + +#[inline] +const fn verbatim<const N: usize>(a: ascii::Char) -> ([ascii::Char; N], Range<u8>) { + const { assert!(N >= 1) }; + + let mut output = [ascii::Char::Null; N]; + + output[0] = a; + + (output, 0..1) +} + /// Escapes an ASCII character. /// /// Returns a buffer and the length of the escaped representation. const fn escape_ascii<const N: usize>(byte: u8) -> ([ascii::Char; N], Range<u8>) { const { assert!(N >= 4) }; - match byte { - b'\t' => backslash(ascii::Char::SmallT), - b'\r' => backslash(ascii::Char::SmallR), - b'\n' => backslash(ascii::Char::SmallN), - b'\\' => backslash(ascii::Char::ReverseSolidus), - b'\'' => backslash(ascii::Char::Apostrophe), - b'\"' => backslash(ascii::Char::QuotationMark), - byte => { - let mut output = [ascii::Char::Null; N]; - - if let Some(c) = byte.as_ascii() - && !byte.is_ascii_control() - { - output[0] = c; - (output, 0..1) - } else { - let hi = HEX_DIGITS[(byte >> 4) as usize]; - let lo = HEX_DIGITS[(byte & 0xf) as usize]; + #[cfg(feature = "optimize_for_size")] + { + match byte { + b'\t' => backslash(ascii::Char::SmallT), + b'\r' => backslash(ascii::Char::SmallR), + b'\n' => backslash(ascii::Char::SmallN), + b'\\' => backslash(ascii::Char::ReverseSolidus), + b'\'' => backslash(ascii::Char::Apostrophe), + b'"' => backslash(ascii::Char::QuotationMark), + 0x00..=0x1F | 0x7F => hex_escape(byte), + _ => match ascii::Char::from_u8(byte) { + Some(a) => verbatim(a), + None => hex_escape(byte), + }, + } + } + + #[cfg(not(feature = "optimize_for_size"))] + { + /// Lookup table helps us determine how to display character. + /// + /// Since ASCII characters will always be 7 bits, we can exploit this to store the 8th bit to + /// indicate whether the result is escaped or unescaped. + /// + /// We additionally use 0x80 (escaped NUL character) to indicate hex-escaped bytes, since + /// escaped NUL will not occur. + const LOOKUP: [u8; 256] = { + let mut arr = [0; 256]; + let mut idx = 0; + while idx <= 255 { + arr[idx] = match idx as u8 { + // use 8th bit to indicate escaped + b'\t' => 0x80 | b't', + b'\r' => 0x80 | b'r', + b'\n' => 0x80 | b'n', + b'\\' => 0x80 | b'\\', + b'\'' => 0x80 | b'\'', + b'"' => 0x80 | b'"', + + // use NUL to indicate hex-escaped + 0x00..=0x1F | 0x7F..=0xFF => 0x80 | b'\0', + + idx => idx, + }; + idx += 1; + } + arr + }; - output[0] = ascii::Char::ReverseSolidus; - output[1] = ascii::Char::SmallX; - output[2] = hi; - output[3] = lo; + let lookup = LOOKUP[byte as usize]; - (output, 0..4) + // 8th bit indicates escape + let lookup_escaped = lookup & 0x80 != 0; + + // SAFETY: We explicitly mask out the eighth bit to get a 7-bit ASCII character. + let lookup_ascii = unsafe { ascii::Char::from_u8_unchecked(lookup & 0x7F) }; + + if lookup_escaped { + // NUL indicates hex-escaped + if matches!(lookup_ascii, ascii::Char::Null) { + hex_escape(byte) + } else { + backslash(lookup_ascii) } + } else { + verbatim(lookup_ascii) } } } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 3ae729e76e1..e8f5af863c1 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -64,6 +64,7 @@ #![allow(missing_docs)] use crate::marker::{DiscriminantKind, Tuple}; +use crate::mem::SizedTypeProperties; use crate::{ptr, ub_checks}; pub mod mir; @@ -1084,7 +1085,7 @@ extern "rust-intrinsic" { /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. - #[rustc_const_unstable(feature = "const_intrinsic_forget", issue = "none")] + #[rustc_const_stable(feature = "const_intrinsic_forget", since = "CURRENT_RUSTC_VERSION")] #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn forget<T: ?Sized>(_: T); @@ -2540,7 +2541,7 @@ extern "rust-intrinsic" { /// This intrinsic can *only* be called where the pointer is a local without /// projections (`write_via_move(ptr, x)`, not `write_via_move(*ptr, x)`) so /// that it trivially obeys runtime-MIR rules about derefs in operands. - #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] + #[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] #[rustc_nounwind] pub fn write_via_move<T>(ptr: *mut T, value: T); @@ -3216,10 +3217,12 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us size: usize = size_of::<T>(), align: usize = align_of::<T>(), count: usize = count, - ) => - ub_checks::is_aligned_and_not_null(src, align) - && ub_checks::is_aligned_and_not_null(dst, align) - && ub_checks::is_nonoverlapping(src, dst, size, count) + ) => { + let zero_size = count == 0 || size == 0; + ub_checks::is_aligned_and_not_null(src, align, zero_size) + && ub_checks::is_aligned_and_not_null(dst, align, zero_size) + && ub_checks::is_nonoverlapping(src, dst, size, count) + } ); // SAFETY: the safety contract for `copy_nonoverlapping` must be @@ -3317,9 +3320,10 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { src: *const () = src as *const (), dst: *mut () = dst as *mut (), align: usize = align_of::<T>(), + zero_size: bool = T::IS_ZST || count == 0, ) => - ub_checks::is_aligned_and_not_null(src, align) - && ub_checks::is_aligned_and_not_null(dst, align) + ub_checks::is_aligned_and_not_null(src, align, zero_size) + && ub_checks::is_aligned_and_not_null(dst, align, zero_size) ); copy(src, dst, count) } @@ -3377,13 +3381,13 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { #[doc(alias = "memset")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_allowed_through_unstable_modules] -#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] +#[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_diagnostic_item = "ptr_write_bytes"] pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) { extern "rust-intrinsic" { - #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] + #[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] #[rustc_nounwind] fn write_bytes<T>(dst: *mut T, val: u8, count: usize); } @@ -3396,7 +3400,8 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) { ( addr: *const () = dst as *const (), align: usize = align_of::<T>(), - ) => ub_checks::is_aligned_and_not_null(addr, align) + zero_size: bool = T::IS_ZST || count == 0, + ) => ub_checks::is_aligned_and_not_null(addr, align, zero_size) ); write_bytes(dst, val, count) } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 482e1f30b7e..ccd6534de9b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -125,23 +125,15 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_index_range_slice_index)] -#![feature(const_intrinsic_forget)] -#![feature(const_ipv4)] -#![feature(const_ipv6)] #![feature(const_likely)] -#![feature(const_make_ascii)] -#![feature(const_maybe_uninit_assume_init)] #![feature(const_nonnull_new)] #![feature(const_num_midpoint)] -#![feature(const_option)] #![feature(const_option_ext)] #![feature(const_pin)] #![feature(const_pointer_is_aligned)] #![feature(const_ptr_is_null)] #![feature(const_ptr_sub_ptr)] -#![feature(const_ptr_write)] #![feature(const_raw_ptr_comparison)] -#![feature(const_replace)] #![feature(const_size_of_val)] #![feature(const_size_of_val_raw)] #![feature(const_strict_overflow_ops)] @@ -183,6 +175,7 @@ // tidy-alphabetical-start #![cfg_attr(bootstrap, feature(const_mut_refs))] #![cfg_attr(bootstrap, feature(const_refs_to_cell))] +#![cfg_attr(bootstrap, feature(const_refs_to_static))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] @@ -277,6 +270,15 @@ pub mod assert_matches { pub use crate::macros::{assert_matches, debug_assert_matches}; } +// We don't export this through #[macro_export] for now, to avoid breakage. +#[cfg(not(bootstrap))] +#[unstable(feature = "autodiff", issue = "124509")] +/// Unstable module containing the unstable `autodiff` macro. +pub mod autodiff { + #[unstable(feature = "autodiff", issue = "124509")] + pub use crate::macros::builtin::autodiff; +} + #[unstable(feature = "cfg_match", issue = "115585")] pub use crate::macros::cfg_match; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index aa0646846e4..6a4f2af10ef 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1107,17 +1107,19 @@ pub(crate) mod builtin { /// /// If the named environment variable is present at compile time, this will /// expand into an expression of type `Option<&'static str>` whose value is - /// `Some` of the value of the environment variable. If the environment - /// variable is not present, then this will expand to `None`. See - /// [`Option<T>`][Option] for more information on this type. Use - /// [`std::env::var`] instead if you want to read the value at runtime. + /// `Some` of the value of the environment variable (a compilation error + /// will be emitted if the environment variable is not a valid Unicode + /// string). If the environment variable is not present, then this will + /// expand to `None`. See [`Option<T>`][Option] for more information on this + /// type. Use [`std::env::var`] instead if you want to read the value at + /// runtime. /// /// [`std::env::var`]: ../std/env/fn.var.html /// - /// A compile time error is never emitted when using this macro regardless - /// of whether the environment variable is present or not. - /// To emit a compile error if the environment variable is not present, - /// use the [`env!`] macro instead. + /// A compile time error is only emitted when using this macro if the + /// environment variable exists and is not a valid Unicode string. To also + /// emit a compile error if the environment variable is not present, use the + /// [`env!`] macro instead. /// /// # Examples /// @@ -1539,6 +1541,24 @@ pub(crate) mod builtin { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } + /// Automatic Differentiation macro which allows generating a new function to compute + /// the derivative of a given function. It may only be applied to a function. + /// The expected usage syntax is + /// `#[autodiff(NAME, MODE, INPUT_ACTIVITIES, OUTPUT_ACTIVITY)]` + /// where: + /// NAME is a string that represents a valid function name. + /// MODE is any of Forward, Reverse, ForwardFirst, ReverseFirst. + /// INPUT_ACTIVITIES consists of one valid activity for each input parameter. + /// OUTPUT_ACTIVITY must not be set if we implicitely return nothing (or explicitely return + /// `-> ()`. Otherwise it must be set to one of the allowed activities. + #[unstable(feature = "autodiff", issue = "124509")] + #[allow_internal_unstable(rustc_attrs)] + #[rustc_builtin_macro] + #[cfg(not(bootstrap))] + pub macro autodiff($item:item) { + /* compiler built-in */ + } + /// Asserts that a boolean expression is `true` at runtime. /// /// This will invoke the [`panic!`] macro if the provided expression cannot be diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index c67796ad3db..f992785c43b 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -913,7 +913,11 @@ impl<T> MaybeUninit<T> { /// }; /// ``` #[stable(feature = "maybe_uninit_ref", since = "1.55.0")] - #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init", issue = "none")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable( + feature = "const_maybe_uninit_assume_init", + since = "CURRENT_RUSTC_VERSION" + )] #[inline(always)] pub const unsafe fn assume_init_mut(&mut self) -> &mut T { // SAFETY: the caller must guarantee that `self` is initialized. @@ -999,7 +1003,8 @@ impl<T> MaybeUninit<T> { /// /// [`assume_init_mut`]: MaybeUninit::assume_init_mut #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init", issue = "none")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] pub const unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T] { // SAFETY: similar to safety notes for `slice_get_ref`, but we have a diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 414262fcf5a..9bf2aa594c0 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -857,7 +857,8 @@ pub fn take<T: Default>(dest: &mut T) -> T { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "if you don't need the old value, you can just assign the new value directly"] -#[rustc_const_unstable(feature = "const_replace", issue = "83164")] +#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] +#[rustc_const_stable(feature = "const_replace", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(not(test), rustc_diagnostic_item = "mem_replace")] pub const fn replace<T>(dest: &mut T, src: T) -> T { // It may be tempting to use `swap` to avoid `unsafe` here. Don't! diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 919f681f911..d3360c18207 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -295,7 +295,6 @@ impl IpAddr { /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true); /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true); /// ``` - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -348,7 +347,6 @@ impl IpAddr { /// true /// ); /// ``` - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -600,6 +598,24 @@ impl Ipv4Addr { self.octets } + /// Creates an `Ipv4Addr` from a four element byte array. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip_from)] + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::from_octets([13u8, 12u8, 11u8, 10u8]); + /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); + /// ``` + #[unstable(feature = "ip_from", issue = "131360")] + #[must_use] + #[inline] + pub const fn from_octets(octets: [u8; 4]) -> Ipv4Addr { + Ipv4Addr { octets } + } + /// Returns [`true`] for the special 'unspecified' address (`0.0.0.0`). /// /// This property is defined in _UNIX Network Programming, Second Edition_, @@ -776,7 +792,6 @@ impl Ipv4Addr { /// /// // For a complete overview see the IANA IPv4 Special-Purpose Address Registry. /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -813,7 +828,6 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true); /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -841,7 +855,6 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true); /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -878,7 +891,6 @@ impl Ipv4Addr { /// // The broadcast address is not considered as reserved for future use by this implementation /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -1400,6 +1412,34 @@ impl Ipv6Addr { ] } + /// Creates an `Ipv6Addr` from an eight element 16-bit array. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip_from)] + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::from_segments([ + /// 0x20du16, 0x20cu16, 0x20bu16, 0x20au16, + /// 0x209u16, 0x208u16, 0x207u16, 0x206u16, + /// ]); + /// assert_eq!( + /// Ipv6Addr::new( + /// 0x20d, 0x20c, 0x20b, 0x20a, + /// 0x209, 0x208, 0x207, 0x206, + /// ), + /// addr + /// ); + /// ``` + #[unstable(feature = "ip_from", issue = "131360")] + #[must_use] + #[inline] + pub const fn from_segments(segments: [u16; 8]) -> Ipv6Addr { + let [a, b, c, d, e, f, g, h] = segments; + Ipv6Addr::new(a, b, c, d, e, f, g, h) + } + /// Returns [`true`] for the special 'unspecified' address (`::`). /// /// This property is defined in [IETF RFC 4291]. @@ -1510,7 +1550,6 @@ impl Ipv6Addr { /// /// // For a complete overview see the IANA IPv6 Special-Purpose Address Registry. /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -1562,7 +1601,6 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false); /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -1591,7 +1629,6 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast(), true); /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_unicast(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -1643,7 +1680,6 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true); /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -1668,7 +1704,6 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -1729,7 +1764,6 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false); /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -1758,7 +1792,6 @@ impl Ipv6Addr { /// ); /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -1818,7 +1851,6 @@ impl Ipv6Addr { /// /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_ipv4_mapped(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] @@ -1932,7 +1964,7 @@ impl Ipv6Addr { /// use std::net::Ipv6Addr; /// /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(), - /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + /// [0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); /// ``` #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] #[stable(feature = "ipv6_to_octets", since = "1.12.0")] @@ -1941,6 +1973,33 @@ impl Ipv6Addr { pub const fn octets(&self) -> [u8; 16] { self.octets } + + /// Creates an `Ipv6Addr` from a sixteen element byte array. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip_from)] + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::from_octets([ + /// 0x19u8, 0x18u8, 0x17u8, 0x16u8, 0x15u8, 0x14u8, 0x13u8, 0x12u8, + /// 0x11u8, 0x10u8, 0x0fu8, 0x0eu8, 0x0du8, 0x0cu8, 0x0bu8, 0x0au8, + /// ]); + /// assert_eq!( + /// Ipv6Addr::new( + /// 0x1918, 0x1716, 0x1514, 0x1312, + /// 0x1110, 0x0f0e, 0x0d0c, 0x0b0a, + /// ), + /// addr + /// ); + /// ``` + #[unstable(feature = "ip_from", issue = "131360")] + #[must_use] + #[inline] + pub const fn from_octets(octets: [u8; 16]) -> Ipv6Addr { + Ipv6Addr { octets } + } } /// Writes an Ipv6Addr, conforming to the canonical style described by @@ -2113,15 +2172,13 @@ impl From<[u8; 16]> for Ipv6Addr { /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::from([ - /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, - /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, + /// 0x19u8, 0x18u8, 0x17u8, 0x16u8, 0x15u8, 0x14u8, 0x13u8, 0x12u8, + /// 0x11u8, 0x10u8, 0x0fu8, 0x0eu8, 0x0du8, 0x0cu8, 0x0bu8, 0x0au8, /// ]); /// assert_eq!( /// Ipv6Addr::new( - /// 0x1918, 0x1716, - /// 0x1514, 0x1312, - /// 0x1110, 0x0f0e, - /// 0x0d0c, 0x0b0a + /// 0x1918, 0x1716, 0x1514, 0x1312, + /// 0x1110, 0x0f0e, 0x0d0c, 0x0b0a, /// ), /// addr /// ); @@ -2142,15 +2199,13 @@ impl From<[u16; 8]> for Ipv6Addr { /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::from([ - /// 525u16, 524u16, 523u16, 522u16, - /// 521u16, 520u16, 519u16, 518u16, + /// 0x20du16, 0x20cu16, 0x20bu16, 0x20au16, + /// 0x209u16, 0x208u16, 0x207u16, 0x206u16, /// ]); /// assert_eq!( /// Ipv6Addr::new( - /// 0x20d, 0x20c, - /// 0x20b, 0x20a, - /// 0x209, 0x208, - /// 0x207, 0x206 + /// 0x20d, 0x20c, 0x20b, 0x20a, + /// 0x209, 0x208, 0x207, 0x206, /// ), /// addr /// ); @@ -2172,15 +2227,13 @@ impl From<[u8; 16]> for IpAddr { /// use std::net::{IpAddr, Ipv6Addr}; /// /// let addr = IpAddr::from([ - /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, - /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, + /// 0x19u8, 0x18u8, 0x17u8, 0x16u8, 0x15u8, 0x14u8, 0x13u8, 0x12u8, + /// 0x11u8, 0x10u8, 0x0fu8, 0x0eu8, 0x0du8, 0x0cu8, 0x0bu8, 0x0au8, /// ]); /// assert_eq!( /// IpAddr::V6(Ipv6Addr::new( - /// 0x1918, 0x1716, - /// 0x1514, 0x1312, - /// 0x1110, 0x0f0e, - /// 0x0d0c, 0x0b0a + /// 0x1918, 0x1716, 0x1514, 0x1312, + /// 0x1110, 0x0f0e, 0x0d0c, 0x0b0a, /// )), /// addr /// ); @@ -2201,15 +2254,13 @@ impl From<[u16; 8]> for IpAddr { /// use std::net::{IpAddr, Ipv6Addr}; /// /// let addr = IpAddr::from([ - /// 525u16, 524u16, 523u16, 522u16, - /// 521u16, 520u16, 519u16, 518u16, + /// 0x20du16, 0x20cu16, 0x20bu16, 0x20au16, + /// 0x209u16, 0x208u16, 0x207u16, 0x206u16, /// ]); /// assert_eq!( /// IpAddr::V6(Ipv6Addr::new( - /// 0x20d, 0x20c, - /// 0x20b, 0x20a, - /// 0x209, 0x208, - /// 0x207, 0x206 + /// 0x20d, 0x20c, 0x20b, 0x20a, + /// 0x209, 0x208, 0x207, 0x206, /// )), /// addr /// ); diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 31e35015d2d..f55d55171f2 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -624,8 +624,9 @@ impl u8 { /// /// [`to_ascii_uppercase`]: Self::to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); } @@ -650,8 +651,9 @@ impl u8 { /// /// [`to_ascii_lowercase`]: Self::to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); } diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 154e52e288b..84ccb7a1f66 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -723,7 +723,8 @@ impl<T> Option<T> { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const fn as_mut(&mut self) -> Option<&mut T> { match *self { Some(ref mut x) => Some(x), @@ -924,7 +925,8 @@ impl<T> Option<T> { #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "option_expect")] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const fn expect(self, msg: &str) -> T { match self { Some(val) => val, @@ -962,7 +964,8 @@ impl<T> Option<T> { #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "option_unwrap")] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const fn unwrap(self) -> T { match self { Some(val) => val, @@ -1069,7 +1072,8 @@ impl<T> Option<T> { #[inline] #[track_caller] #[stable(feature = "option_result_unwrap_unchecked", since = "1.58.0")] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn unwrap_unchecked(self) -> T { match self { Some(val) => val, @@ -1712,7 +1716,8 @@ impl<T> Option<T> { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const fn take(&mut self) -> Option<T> { // FIXME(const-hack) replace `mem::replace` by `mem::take` when the latter is const ready mem::replace(self, None) @@ -1769,8 +1774,9 @@ impl<T> Option<T> { /// assert_eq!(old, None); /// ``` #[inline] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] #[stable(feature = "option_replace", since = "1.31.0")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const fn replace(&mut self, value: T) -> Option<T> { mem::replace(self, Some(value)) } @@ -1878,7 +1884,7 @@ impl<T> Option<&T> { /// ``` #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "copied", since = "1.35.0")] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const fn copied(self) -> Option<T> where T: Copy, @@ -1931,7 +1937,8 @@ impl<T> Option<&mut T> { /// ``` #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "copied", since = "1.35.0")] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const fn copied(self) -> Option<T> where T: Copy, @@ -1986,7 +1993,8 @@ impl<T, E> Option<Result<T, E>> { /// ``` #[inline] #[stable(feature = "transpose_result", since = "1.33.0")] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const fn transpose(self) -> Result<Option<T>, E> { match self { Some(Ok(x)) => Ok(Some(x)), @@ -2009,7 +2017,6 @@ const fn unwrap_failed() -> ! { #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -#[rustc_const_unstable(feature = "const_option", issue = "67441")] const fn expect_failed(msg: &str) -> ! { panic_display(&msg) } @@ -2534,7 +2541,8 @@ impl<T> Option<Option<T>> { /// ``` #[inline] #[stable(feature = "option_flattening", since = "1.40.0")] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + #[rustc_const_stable(feature = "const_option", since = "CURRENT_RUSTC_VERSION")] pub const fn flatten(self) -> Option<T> { // FIXME(const-hack): could be written with `and_then` match self { diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 9c13662e08e..fac789dbd99 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1422,7 +1422,7 @@ impl<Ptr: DerefMut> Pin<Ptr> { /// move in the future, and this method does not enable the pointee to move. "Malicious" /// implementations of `Ptr::DerefMut` are likewise ruled out by the contract of /// `Pin::new_unchecked`. - #[unstable(feature = "pin_deref_mut", issue = "86918")] + #[stable(feature = "pin_deref_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "`self` will be dropped if the result is not used"] #[inline(always)] pub fn as_deref_mut(self: Pin<&mut Pin<Ptr>>) -> Pin<&mut Ptr::Target> { diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index c9af7f13e46..0b0449d6174 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -63,21 +63,22 @@ impl<T: ?Sized> *const T { self as _ } - /// Uses the pointer value in a new pointer of another type. + /// Uses the address value in a new pointer of another type. /// - /// In case `meta` is a (fat) pointer to an unsized type, this operation - /// will ignore the pointer part, whereas for (thin) pointers to sized - /// types, this has the same effect as a simple cast. + /// This operation will ignore the address part of its `meta` operand and discard existing + /// metadata of `self`. For pointers to a sized types (thin pointers), this has the same effect + /// as a simple cast. For pointers to an unsized type (fat pointers) this recombines the address + /// with new metadata such as slice lengths or `dyn`-vtable. /// - /// The resulting pointer will have provenance of `self`, i.e., for a fat - /// pointer, this operation is semantically the same as creating a new - /// fat pointer with the data pointer value of `self` but the metadata of - /// `meta`. + /// The resulting pointer will have provenance of `self`. This operation is semantically the + /// same as creating a new pointer with the data pointer value of `self` but the metadata of + /// `meta`, being fat or thin depending on the `meta` operand. /// /// # Examples /// - /// This function is primarily useful for allowing byte-wise pointer - /// arithmetic on potentially fat pointers: + /// This function is primarily useful for enabling pointer arithmetic on potentially fat + /// pointers. The pointer is cast to a sized pointee to utilize offset operations and then + /// recombined with its own original metadata. /// /// ``` /// #![feature(set_ptr_value)] @@ -91,6 +92,26 @@ impl<T: ?Sized> *const T { /// println!("{:?}", &*ptr); // will print "3" /// } /// ``` + /// + /// # *Incorrect* usage + /// + /// The provenance from pointers is *not* combined. The result must only be used to refer to the + /// address allowed by `self`. + /// + /// ```rust,no_run + /// #![feature(set_ptr_value)] + /// let x = 0u32; + /// let y = 1u32; + /// + /// let x = (&x) as *const u32; + /// let y = (&y) as *const u32; + /// + /// let offset = (x as usize - y as usize) / 4; + /// let bad = x.wrapping_add(offset).with_metadata_of(y); + /// + /// // This dereference is UB. The pointer only has provenance for `x` but points to `y`. + /// println!("{:?}", unsafe { &*bad }); + /// ``` #[unstable(feature = "set_ptr_value", issue = "75091")] #[rustc_const_stable(feature = "ptr_metadata_const", since = "CURRENT_RUSTC_VERSION")] #[must_use = "returns a new pointer rather than modifying its argument"] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 67f1b0cd16d..dd93f919c92 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -448,7 +448,7 @@ use crate::cmp::Ordering; use crate::marker::FnPtr; -use crate::mem::{self, MaybeUninit}; +use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::{fmt, hash, intrinsics, ub_checks}; mod alignment; @@ -1165,10 +1165,12 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) { size: usize = size_of::<T>(), align: usize = align_of::<T>(), count: usize = count, - ) => - ub_checks::is_aligned_and_not_null(x, align) - && ub_checks::is_aligned_and_not_null(y, align) - && ub_checks::is_nonoverlapping(x, y, size, count) + ) => { + let zero_size = size == 0 || count == 0; + ub_checks::is_aligned_and_not_null(x, align, zero_size) + && ub_checks::is_aligned_and_not_null(y, align, zero_size) + && ub_checks::is_nonoverlapping(x, y, size, count) + } ); // Split up the slice into small power-of-two-sized chunks that LLVM is able @@ -1263,7 +1265,8 @@ const unsafe fn swap_nonoverlapping_simple_untyped<T>(x: *mut T, y: *mut T, coun /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_replace", issue = "83164")] +#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] +#[rustc_const_stable(feature = "const_replace", since = "CURRENT_RUSTC_VERSION")] #[rustc_diagnostic_item = "ptr_replace"] pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T { // SAFETY: the caller must guarantee that `dst` is valid to be @@ -1277,7 +1280,8 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T { ( addr: *const () = dst as *const (), align: usize = align_of::<T>(), - ) => ub_checks::is_aligned_and_not_null(addr, align) + is_zst: bool = T::IS_ZST, + ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst) ); mem::replace(&mut *dst, src) } @@ -1429,7 +1433,8 @@ pub const unsafe fn read<T>(src: *const T) -> T { ( addr: *const () = src as *const (), align: usize = align_of::<T>(), - ) => ub_checks::is_aligned_and_not_null(addr, align) + is_zst: bool = T::IS_ZST, + ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst) ); crate::intrinsics::read_via_copy(src) } @@ -1611,7 +1616,7 @@ pub const unsafe fn read_unaligned<T>(src: *const T) -> T { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] +#[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] #[rustc_diagnostic_item = "ptr_write"] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn write<T>(dst: *mut T, src: T) { @@ -1634,7 +1639,8 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) { ( addr: *mut () = dst as *mut (), align: usize = align_of::<T>(), - ) => ub_checks::is_aligned_and_not_null(addr, align) + is_zst: bool = T::IS_ZST, + ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst) ); intrinsics::write_via_move(dst, src) } @@ -1719,7 +1725,8 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) { /// ``` #[inline] #[stable(feature = "ptr_unaligned", since = "1.17.0")] -#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] +#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_refs_to_cell))] +#[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] #[rustc_diagnostic_item = "ptr_write_unaligned"] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) { @@ -1806,7 +1813,8 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T { ( addr: *const () = src as *const (), align: usize = align_of::<T>(), - ) => ub_checks::is_aligned_and_not_null(addr, align) + is_zst: bool = T::IS_ZST, + ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst) ); intrinsics::volatile_load(src) } @@ -1885,7 +1893,8 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) { ( addr: *mut () = dst as *mut (), align: usize = align_of::<T>(), - ) => ub_checks::is_aligned_and_not_null(addr, align) + is_zst: bool = T::IS_ZST, + ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst) ); intrinsics::volatile_store(dst, src); } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index e458bb4642f..8b61da74d02 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -45,21 +45,22 @@ impl<T: ?Sized> *mut T { self as _ } - /// Uses the pointer value in a new pointer of another type. + /// Uses the address value in a new pointer of another type. /// - /// In case `meta` is a (fat) pointer to an unsized type, this operation - /// will ignore the pointer part, whereas for (thin) pointers to sized - /// types, this has the same effect as a simple cast. + /// This operation will ignore the address part of its `meta` operand and discard existing + /// metadata of `self`. For pointers to a sized types (thin pointers), this has the same effect + /// as a simple cast. For pointers to an unsized type (fat pointers) this recombines the address + /// with new metadata such as slice lengths or `dyn`-vtable. /// - /// The resulting pointer will have provenance of `self`, i.e., for a fat - /// pointer, this operation is semantically the same as creating a new - /// fat pointer with the data pointer value of `self` but the metadata of - /// `meta`. + /// The resulting pointer will have provenance of `self`. This operation is semantically the + /// same as creating a new pointer with the data pointer value of `self` but the metadata of + /// `meta`, being fat or thin depending on the `meta` operand. /// /// # Examples /// - /// This function is primarily useful for allowing byte-wise pointer - /// arithmetic on potentially fat pointers: + /// This function is primarily useful for enabling pointer arithmetic on potentially fat + /// pointers. The pointer is cast to a sized pointee to utilize offset operations and then + /// recombined with its own original metadata. /// /// ``` /// #![feature(set_ptr_value)] @@ -73,6 +74,25 @@ impl<T: ?Sized> *mut T { /// println!("{:?}", &*ptr); // will print "3" /// } /// ``` + /// + /// # *Incorrect* usage + /// + /// The provenance from pointers is *not* combined. The result must only be used to refer to the + /// address allowed by `self`. + /// + /// ```rust,no_run + /// #![feature(set_ptr_value)] + /// let mut x = 0u32; + /// let mut y = 1u32; + /// + /// let x = (&mut x) as *mut u32; + /// let y = (&mut y) as *mut u32; + /// + /// let offset = (x as usize - y as usize) / 4; + /// let bad = x.wrapping_add(offset).with_metadata_of(y); + /// + /// // This dereference is UB. The pointer only has provenance for `x` but points to `y`. + /// println!("{:?}", unsafe { &*bad }); #[unstable(feature = "set_ptr_value", issue = "75091")] #[rustc_const_stable(feature = "ptr_metadata_const", since = "CURRENT_RUSTC_VERSION")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1449,7 +1469,7 @@ impl<T: ?Sized> *mut T { /// /// [`ptr::write`]: crate::ptr::write() #[stable(feature = "pointer_methods", since = "1.26.0")] - #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] + #[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn write(self, val: T) @@ -1468,7 +1488,7 @@ impl<T: ?Sized> *mut T { /// [`ptr::write_bytes`]: crate::ptr::write_bytes() #[doc(alias = "memset")] #[stable(feature = "pointer_methods", since = "1.26.0")] - #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] + #[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn write_bytes(self, val: u8, count: usize) @@ -1509,7 +1529,7 @@ impl<T: ?Sized> *mut T { /// /// [`ptr::write_unaligned`]: crate::ptr::write_unaligned() #[stable(feature = "pointer_methods", since = "1.26.0")] - #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] + #[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn write_unaligned(self, val: T) diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 6c5834a1ece..980d4a3cf6c 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1013,7 +1013,7 @@ impl<T: ?Sized> NonNull<T> { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] + #[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn write(self, val: T) where T: Sized, @@ -1032,7 +1032,7 @@ impl<T: ?Sized> NonNull<T> { #[doc(alias = "memset")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] + #[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn write_bytes(self, val: u8, count: usize) where T: Sized, @@ -1073,7 +1073,7 @@ impl<T: ?Sized> NonNull<T> { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] + #[rustc_const_stable(feature = "const_ptr_write", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn write_unaligned(self, val: T) where T: Sized, @@ -1211,7 +1211,6 @@ impl<T: ?Sized> NonNull<T> { /// /// ``` /// #![feature(const_nonnull_new)] - /// #![feature(const_option)] /// #![feature(const_pointer_is_aligned)] /// use std::ptr::NonNull; /// @@ -1264,7 +1263,6 @@ impl<T: ?Sized> NonNull<T> { /// /// ``` /// #![feature(const_pointer_is_aligned)] - /// #![feature(const_option)] /// #![feature(const_nonnull_new)] /// use std::ptr::NonNull; /// diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 8f8050fdc3a..8d8ac6a1d2a 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -67,8 +67,9 @@ impl [u8] { /// /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn make_ascii_uppercase(&mut self) { // FIXME(const-hack): We would like to simply iterate using `for` loops but this isn't currently allowed in constant expressions. let mut i = 0; @@ -89,8 +90,9 @@ impl [u8] { /// /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn make_ascii_lowercase(&mut self) { // FIXME(const-hack): We would like to simply iterate using `for` loops but this isn't currently allowed in constant expressions. let mut i = 0; diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 998f9360332..20c5e026946 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -132,7 +132,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] align: usize = align_of::<T>(), len: usize = len, ) => - ub_checks::is_aligned_and_not_null(data, align) + ub_checks::is_aligned_and_not_null(data, align, false) && ub_checks::is_valid_allocation_size(size, len) ); &*ptr::slice_from_raw_parts(data, len) @@ -187,7 +187,7 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m align: usize = align_of::<T>(), len: usize = len, ) => - ub_checks::is_aligned_and_not_null(data, align) + ub_checks::is_aligned_and_not_null(data, align, false) && ub_checks::is_valid_allocation_size(size, len) ); &mut *ptr::slice_from_raw_parts_mut(data, len) diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index e93c52f2799..1d10015d75a 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2475,8 +2475,9 @@ impl str { /// assert_eq!("GRüßE, JüRGEN ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn make_ascii_uppercase(&mut self) { // SAFETY: changing ASCII letters only does not invalidate UTF-8. let me = unsafe { self.as_bytes_mut() }; @@ -2503,8 +2504,9 @@ impl str { /// assert_eq!("grÜße, jÜrgen ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn make_ascii_lowercase(&mut self) { // SAFETY: changing ASCII letters only does not invalidate UTF-8. let me = unsafe { self.as_bytes_mut() }; diff --git a/library/core/src/time.rs b/library/core/src/time.rs index a997874dd9c..51005ff795c 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -213,11 +213,9 @@ impl Duration { // SAFETY: nanos < NANOS_PER_SEC, therefore nanos is within the valid range Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } } else { - // FIXME(const-hack): use `.expect` once that is possible. - let secs = match secs.checked_add((nanos / NANOS_PER_SEC) as u64) { - Some(secs) => secs, - None => panic!("overflow in Duration::new"), - }; + let secs = secs + .checked_add((nanos / NANOS_PER_SEC) as u64) + .expect("overflow in Duration::new"); let nanos = nanos % NANOS_PER_SEC; // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } @@ -626,7 +624,6 @@ impl Duration { /// ``` #[stable(feature = "duration_abs_diff", since = "1.81.0")] #[rustc_const_stable(feature = "duration_abs_diff", since = "1.81.0")] - #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index c1a8c34539e..daaaf5a7195 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -109,15 +109,15 @@ pub(crate) const fn check_language_ub() -> bool { intrinsics::ub_checks() && const_eval_select((), comptime, runtime) } -/// Checks whether `ptr` is properly aligned with respect to -/// `align_of::<T>()`. +/// Checks whether `ptr` is properly aligned with respect to the given alignment, and +/// if `is_zst == false`, that `ptr` is not null. /// /// In `const` this is approximate and can fail spuriously. It is primarily intended /// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the /// check is anyway not executed in `const`. #[inline] -pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { - !ptr.is_null() && ptr.is_aligned_to(align) +pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize, is_zst: bool) -> bool { + ptr.is_aligned_to(align) && (is_zst || !ptr.is_null()) } #[inline] diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index db2e3ddd754..cba53bf5054 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -331,14 +331,14 @@ pub mod grapheme_extend { #[rustfmt::skip] pub mod lowercase { - const BITSET_CHUNKS_MAP: &'static [u8; 123] = &[ + static BITSET_CHUNKS_MAP: [u8; 123] = [ 14, 17, 0, 0, 9, 0, 0, 12, 13, 10, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 15, 0, 8, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 3, 18, 0, 7, ]; - const BITSET_INDEX_CHUNKS: &'static [[u8; 16]; 20] = &[ + static BITSET_INDEX_CHUNKS: [[u8; 16]; 20] = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 56, 0], @@ -360,7 +360,7 @@ pub mod lowercase { [16, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [65, 41, 55, 12, 77, 63, 18, 1, 7, 64, 76, 20, 73, 74, 4, 45], ]; - const BITSET_CANONICAL: &'static [u64; 56] = &[ + static BITSET_CANONICAL: [u64; 56] = [ 0b0000000000000000000000000000000000000000000000000000000000000000, 0b1111111111111111110000000000000000000000000011111111111111111111, 0b1010101010101010101010101010101010101010101010101010100000000010, @@ -418,7 +418,7 @@ pub mod lowercase { 0b1110011001010001001011010010101001001110001001000011000100101001, 0b1110101111000000000000000000000000001111111111111111111111111100, ]; - const BITSET_MAPPING: &'static [(u8, u8); 22] = &[ + static BITSET_MAPPING: [(u8, u8); 22] = [ (0, 64), (1, 188), (1, 186), (1, 183), (1, 176), (1, 109), (1, 124), (1, 126), (1, 66), (1, 70), (1, 77), (2, 146), (2, 144), (2, 83), (3, 93), (3, 147), (3, 133), (4, 12), (4, 6), (5, 187), (6, 78), (7, 132), @@ -471,14 +471,14 @@ pub mod n { #[rustfmt::skip] pub mod uppercase { - const BITSET_CHUNKS_MAP: &'static [u8; 125] = &[ + static BITSET_CHUNKS_MAP: [u8; 125] = [ 12, 15, 6, 6, 0, 6, 6, 2, 4, 11, 6, 16, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 14, 6, 10, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, 6, 6, 9, 6, 3, ]; - const BITSET_INDEX_CHUNKS: &'static [[u8; 16]; 17] = &[ + static BITSET_INDEX_CHUNKS: [[u8; 16]; 17] = [ [44, 44, 5, 35, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 5, 1], [44, 44, 5, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 40, 44, 44, 44, 44, 44, 17, 17, 63, 17, 43, 29, 24, 23], @@ -497,7 +497,7 @@ pub mod uppercase { [58, 19, 2, 18, 10, 48, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [58, 38, 17, 27, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], ]; - const BITSET_CANONICAL: &'static [u64; 44] = &[ + static BITSET_CANONICAL: [u64; 44] = [ 0b0000011111111111111111111111111000000000000000000000000000000000, 0b0000000000111111111111111111111111111111111111111111111111111111, 0b0101010101010101010101010101010101010101010101010101010000000001, @@ -543,7 +543,7 @@ pub mod uppercase { 0b1111011111111111000000000000000000000000000000000000000000000000, 0b1111111100000000111111110000000000111111000000001111111100000000, ]; - const BITSET_MAPPING: &'static [(u8, u8); 25] = &[ + static BITSET_MAPPING: [(u8, u8); 25] = [ (0, 187), (0, 177), (0, 171), (0, 167), (0, 164), (0, 32), (0, 47), (0, 51), (0, 121), (0, 117), (0, 109), (1, 150), (1, 148), (1, 142), (1, 134), (1, 131), (1, 64), (2, 164), (2, 146), (2, 20), (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), diff --git a/library/core/tests/ascii.rs b/library/core/tests/ascii.rs index 3d3f8ac10c6..ce09ee507f1 100644 --- a/library/core/tests/ascii.rs +++ b/library/core/tests/ascii.rs @@ -481,9 +481,25 @@ fn ascii_ctype_const() { } #[test] -fn test_ascii_display() { - assert_eq!(b"foo'bar".escape_ascii().to_string(), r#"foo\'bar"#); - assert_eq!(b"\0\xff".escape_ascii().to_string(), r#"\x00\xff"#); +fn test_escape_ascii() { + let mut buf = [0u8; 0x1F + 7]; // 0..=0x1F plus two quotes, slash, \x7F, \x80, \xFF + for idx in 0..=0x1F { + buf[idx] = idx as u8; + } + buf[0x20] = b'\''; + buf[0x21] = b'"'; + buf[0x22] = b'\\'; + buf[0x23] = 0x7F; + buf[0x24] = 0x80; + buf[0x25] = 0xff; + assert_eq!( + buf.escape_ascii().to_string(), + r#"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\'\"\\\x7f\x80\xff"# + ); +} + +#[test] +fn test_escape_ascii_iter() { let mut it = b"\0fastpath\xffremainder\xff".escape_ascii(); let _ = it.advance_by(4); let _ = it.advance_back_by(4); diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 76efa4b2680..37e7db1157c 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -19,16 +19,11 @@ #![feature(const_black_box)] #![feature(const_hash)] #![feature(const_heap)] -#![feature(const_ip)] -#![feature(const_ipv4)] -#![feature(const_ipv6)] #![feature(const_likely)] #![feature(const_nonnull_new)] -#![feature(const_option)] #![feature(const_option_ext)] #![feature(const_pin)] #![feature(const_pointer_is_aligned)] -#![feature(const_ptr_write)] #![feature(const_three_way_compare)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] @@ -52,6 +47,7 @@ #![feature(hashmap_internals)] #![feature(int_roundings)] #![feature(ip)] +#![feature(ip_from)] #![feature(is_ascii_octdigit)] #![feature(isqrt)] #![feature(iter_advance_by)] diff --git a/library/core/tests/net/ip_addr.rs b/library/core/tests/net/ip_addr.rs index a10b51c550d..707f9a160e1 100644 --- a/library/core/tests/net/ip_addr.rs +++ b/library/core/tests/net/ip_addr.rs @@ -494,6 +494,7 @@ fn ipv6_properties() { let octets = &[$($octet),*]; assert_eq!(&ip!($s).octets(), octets); assert_eq!(Ipv6Addr::from(*octets), ip!($s)); + assert_eq!(Ipv6Addr::from_octets(*octets), ip!($s)); let unspecified: u32 = 1 << 0; let loopback: u32 = 1 << 1; @@ -846,15 +847,19 @@ fn ipv6_from_constructors() { #[test] fn ipv4_from_octets() { - assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1)) + assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1)); + assert_eq!(Ipv4Addr::from_octets([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1)); } #[test] fn ipv6_from_segments() { let from_u16s = Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); + let from_u16s_explicit = + Ipv6Addr::from_segments([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff); assert_eq!(new, from_u16s); + assert_eq!(new, from_u16s_explicit); } #[test] @@ -865,7 +870,15 @@ fn ipv6_from_octets() { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, ]); + let from_u16s_explicit = + Ipv6Addr::from_segments([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); + let from_u8s_explicit = Ipv6Addr::from_octets([ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, + 0xff, + ]); assert_eq!(from_u16s, from_u8s); + assert_eq!(from_u16s, from_u16s_explicit); + assert_eq!(from_u16s_explicit, from_u8s_explicit); } #[test] @@ -915,6 +928,9 @@ fn ipv4_const() { const OCTETS: [u8; 4] = IP_ADDRESS.octets(); assert_eq!(OCTETS, [127, 0, 0, 1]); + const FROM_OCTETS: Ipv4Addr = Ipv4Addr::from_octets(OCTETS); + assert_eq!(IP_ADDRESS, FROM_OCTETS); + const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); assert!(!IS_UNSPECIFIED); @@ -971,9 +987,15 @@ fn ipv6_const() { const SEGMENTS: [u16; 8] = IP_ADDRESS.segments(); assert_eq!(SEGMENTS, [0, 0, 0, 0, 0, 0, 0, 1]); + const FROM_SEGMENTS: Ipv6Addr = Ipv6Addr::from_segments(SEGMENTS); + assert_eq!(IP_ADDRESS, FROM_SEGMENTS); + const OCTETS: [u8; 16] = IP_ADDRESS.octets(); assert_eq!(OCTETS, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + const FROM_OCTETS: Ipv6Addr = Ipv6Addr::from_octets(OCTETS); + assert_eq!(IP_ADDRESS, FROM_OCTETS); + const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); assert!(!IS_UNSPECIFIED); diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index f2669326065..358bd25ff1b 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -39,7 +39,7 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.156", default-features = false, features = [ +libc = { version = "0.2.159", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index bf242e715bd..35b38ed783f 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -370,7 +370,12 @@ impl Stdin { /// Locks this handle and reads a line of input, appending it to the specified buffer. /// /// For detailed semantics of this method, see the documentation on - /// [`BufRead::read_line`]. + /// [`BufRead::read_line`]. In particular: + /// * Previous content of the buffer will be preserved. To avoid appending + /// to the buffer, you need to [`clear`] it first. + /// * The trailing newline character, if any, is included in the buffer. + /// + /// [`clear`]: String::clear /// /// # Examples /// diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 990d83513cf..ed448e50e60 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -267,6 +267,7 @@ #![allow(unused_features)] // // Features: +#![cfg_attr(not(bootstrap), feature(autodiff))] #![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), @@ -415,9 +416,6 @@ // tidy-alphabetical-start #![feature(const_collections_with_hasher)] #![feature(const_hash)] -#![feature(const_ip)] -#![feature(const_ipv4)] -#![feature(const_ipv6)] #![feature(thread_local_internals)] // tidy-alphabetical-end // @@ -628,7 +626,13 @@ pub mod simd { #[doc(inline)] pub use crate::std_float::StdFloat; } - +#[cfg(not(bootstrap))] +#[unstable(feature = "autodiff", issue = "124509")] +/// This module provides support for automatic differentiation. +pub mod autodiff { + /// This macro handles automatic differentiation. + pub use core::autodiff::autodiff; +} #[stable(feature = "futures_api", since = "1.36.0")] pub mod task { //! Types and Traits for working with asynchronous tasks. diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 0a841f07e3b..80e7c3c026b 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -102,9 +102,24 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { sys::init(argc, argv, sigpipe) }; - // Set up the current thread to give it the right name. - let thread = Thread::new_main(); - thread::set_current(thread); + // Set up the current thread handle to give it the right name. + // + // When code running before main uses `ReentrantLock` (for example by + // using `println!`), the thread ID can become initialized before we + // create this handle. Since `set_current` fails when the ID of the + // handle does not match the current ID, we should attempt to use the + // current thread ID here instead of unconditionally creating a new + // one. Also see #130210. + let thread = Thread::new_main(thread::current_id()); + if let Err(_thread) = thread::set_current(thread) { + // `thread::current` will create a new handle if none has been set yet. + // Thus, if someone uses it before main, this call will fail. That's a + // bad idea though, as we then cannot set the main thread name here. + // + // FIXME: detect the main thread in `thread::current` and use the + // correct name there. + rtabort!("code running before main must not use thread::current"); + } } /// Clean up the thread-local runtime state. This *should* be run after all other diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index bd8a6684b64..4ced7065c82 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -177,16 +177,8 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R ) }; - // SAFETY: `convert_device_path_to_text` returns a pointer to a null-terminated UTF-16 - // string, and that string cannot be deallocated prior to dropping the `WStrUnits`, so - // it's safe for `WStrUnits` to use. - let path_len = unsafe { - WStrUnits::new(path_ptr) - .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))? - .count() - }; - - let path = OsString::from_wide(unsafe { slice::from_raw_parts(path_ptr.cast(), path_len) }); + let path = os_string_from_raw(path_ptr) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?; if let Some(boot_services) = crate::os::uefi::env::boot_services() { let boot_services: NonNull<r_efi::efi::BootServices> = boot_services.cast(); @@ -420,3 +412,15 @@ impl<T> Drop for OwnedTable<T> { unsafe { crate::alloc::dealloc(self.ptr as *mut u8, self.layout) }; } } + +/// Create OsString from a pointer to NULL terminated UTF-16 string +pub(crate) fn os_string_from_raw(ptr: *mut r_efi::efi::Char16) -> Option<OsString> { + let path_len = unsafe { WStrUnits::new(ptr)?.count() }; + Some(OsString::from_wide(unsafe { slice::from_raw_parts(ptr.cast(), path_len) })) +} + +/// Create NULL terminated UTF-16 string +pub(crate) fn os_string_to_raw(s: &OsStr) -> Option<Box<[r_efi::efi::Char16]>> { + let temp = s.encode_wide().chain(Some(0)).collect::<Box<[r_efi::efi::Char16]>>(); + if temp[..temp.len() - 1].contains(&0) { None } else { Some(temp) } +} diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 9aee67d622f..4eb7698b43a 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,7 +1,7 @@ use r_efi::efi::Status; use r_efi::efi::protocols::{device_path, loaded_image_device_path}; -use super::{RawOsError, helpers, unsupported}; +use super::{RawOsError, helpers, unsupported_err}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; @@ -125,11 +125,32 @@ pub fn error_string(errno: RawOsError) -> String { } pub fn getcwd() -> io::Result<PathBuf> { - unsupported() + match uefi_shell::open_shell() { + Some(shell) => { + // SAFETY: path_ptr is managed by UEFI shell and should not be deallocated + let path_ptr = unsafe { ((*shell.as_ptr()).get_cur_dir)(crate::ptr::null_mut()) }; + helpers::os_string_from_raw(path_ptr) + .map(PathBuf::from) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path")) + } + None => { + let mut t = current_exe()?; + // SAFETY: This should never fail since the disk prefix will be present even for root + // executables + assert!(t.pop()); + Ok(t) + } + } } -pub fn chdir(_: &path::Path) -> io::Result<()> { - unsupported() +pub fn chdir(p: &path::Path) -> io::Result<()> { + let shell = uefi_shell::open_shell().ok_or(unsupported_err())?; + + let mut p = helpers::os_string_to_raw(p.as_os_str()) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?; + + let r = unsafe { ((*shell.as_ptr()).set_cur_dir)(crate::ptr::null_mut(), p.as_mut_ptr()) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); @@ -239,3 +260,37 @@ pub fn exit(code: i32) -> ! { pub fn getpid() -> u32 { panic!("no pids on this platform") } + +mod uefi_shell { + use r_efi::protocols::shell; + + use super::super::helpers; + use crate::ptr::NonNull; + use crate::sync::atomic::{AtomicPtr, Ordering}; + + pub fn open_shell() -> Option<NonNull<shell::Protocol>> { + static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = helpers::open_protocol::<shell::Protocol>( + handle, + r_efi::protocols::shell::PROTOCOL_GUID, + ) { + return Some(protocol); + } + } + + let handles = helpers::locate_handles(shell::PROTOCOL_GUID).ok()?; + for handle in handles { + if let Ok(protocol) = + helpers::open_protocol::<shell::Protocol>(handle, shell::PROTOCOL_GUID) + { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return Some(protocol); + } + } + + None + } +} diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 39aabf0b2d6..567577b2b4d 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -899,7 +899,7 @@ impl DirEntry { target_os = "android", target_os = "hurd" ), - not(miri) + not(miri) // no dirfd on Miri ))] pub fn metadata(&self) -> io::Result<FileAttr> { let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; @@ -1538,7 +1538,7 @@ impl fmt::Debug for File { Some(PathBuf::from(OsString::from_vec(buf))) } - #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] + #[cfg(target_os = "freebsd")] fn get_path(fd: c_int) -> Option<PathBuf> { let info = Box::<libc::kinfo_file>::new_zeroed(); let mut info = unsafe { info.assume_init() }; @@ -1566,7 +1566,7 @@ impl fmt::Debug for File { #[cfg(not(any( target_os = "linux", target_os = "vxworks", - all(target_os = "freebsd", target_arch = "x86_64"), + target_os = "freebsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/pal/windows/args.rs index 848632ec2a7..e9fc19bcb99 100644 --- a/library/std/src/sys/pal/windows/args.rs +++ b/library/std/src/sys/pal/windows/args.rs @@ -18,17 +18,6 @@ use crate::sys_common::AsInner; use crate::sys_common::wstr::WStrUnits; use crate::{fmt, io, iter, vec}; -/// This is the const equivalent to `NonZero::new(n).unwrap()` -/// -/// FIXME(const-hack): This can be removed once `Option::unwrap` is stably const. -/// See the `const_option` feature (#67441). -const fn non_zero_u16(n: u16) -> NonZero<u16> { - match NonZero::new(n) { - Some(n) => n, - None => panic!("called `unwrap` on a `None` value"), - } -} - pub fn args() -> Args { // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16 // string so it's safe for `WStrUnits` to use. @@ -66,10 +55,10 @@ fn parse_lp_cmd_line<'a, F: Fn() -> OsString>( lp_cmd_line: Option<WStrUnits<'a>>, exe_name: F, ) -> Vec<OsString> { - const BACKSLASH: NonZero<u16> = non_zero_u16(b'\\' as u16); - const QUOTE: NonZero<u16> = non_zero_u16(b'"' as u16); - const TAB: NonZero<u16> = non_zero_u16(b'\t' as u16); - const SPACE: NonZero<u16> = non_zero_u16(b' ' as u16); + const BACKSLASH: NonZero<u16> = NonZero::new(b'\\' as u16).unwrap(); + const QUOTE: NonZero<u16> = NonZero::new(b'"' as u16).unwrap(); + const TAB: NonZero<u16> = NonZero::new(b'\t' as u16).unwrap(); + const SPACE: NonZero<u16> = NonZero::new(b' ' as u16).unwrap(); let mut ret_val = Vec::new(); // If the cmd line pointer is null or it points to an empty string then diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index b65ad7dbe8c..9ce3e912caf 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -175,9 +175,9 @@ extern "system" { pub fn WakeByAddressAll(address: *const c_void); } +// These are loaded by `load_synch_functions`. #[cfg(target_vendor = "win7")] compat_fn_optional! { - crate::sys::compat::load_synch_functions(); pub fn WaitOnAddress( address: *const c_void, compareaddress: *const c_void, diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index c8e25dd0c94..42999da1664 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -198,11 +198,10 @@ macro_rules! compat_fn_with_fallback { /// Optionally loaded functions. /// -/// Actual loading of the function defers to $load_functions. +/// Relies on the functions being pre-loaded elsewhere. #[cfg(target_vendor = "win7")] macro_rules! compat_fn_optional { - ($load_functions:expr; - $( + ($( $(#[$meta:meta])* $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) $(-> $rettype:ty)?; )+) => ( @@ -221,9 +220,6 @@ macro_rules! compat_fn_optional { #[inline(always)] pub fn option() -> Option<F> { - // Miri does not understand the way we do preloading - // therefore load the function here instead. - #[cfg(miri)] $load_functions; NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) }) } } diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 1ea253e5e52..a9886012e8e 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -346,7 +346,6 @@ pub fn abort_internal() -> ! { } } -// miri is sensitive to changes here so check that miri is happy if touching this #[cfg(miri)] pub fn abort_internal() -> ! { crate::intrinsics::abort(); diff --git a/library/std/src/sys/sync/thread_parking/mod.rs b/library/std/src/sys/sync/thread_parking/mod.rs index 0ebc5e093ee..f4d8fa0a58c 100644 --- a/library/std/src/sys/sync/thread_parking/mod.rs +++ b/library/std/src/sys/sync/thread_parking/mod.rs @@ -23,6 +23,7 @@ cfg_if::cfg_if! { mod windows7; pub use windows7::Parker; } else if #[cfg(all(target_vendor = "apple", not(miri)))] { + // Doesn't work in Miri, see <https://github.com/rust-lang/miri/issues/2589>. mod darwin; pub use darwin::Parker; } else if #[cfg(target_os = "xous")] { diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs index b38149a0da7..e6eb90c4c30 100644 --- a/library/std/src/thread/current.rs +++ b/library/std/src/thread/current.rs @@ -110,22 +110,24 @@ mod id { } } -/// Sets the thread handle for the current thread. -/// -/// Aborts if the handle or the ID has been set already. -pub(crate) fn set_current(thread: Thread) { - if CURRENT.get() != NONE || id::get().is_some() { - // Using `panic` here can add ~3kB to the binary size. We have complete - // control over where this is called, so just abort if there is a bug. - rtabort!("thread::set_current should only be called once per thread"); +/// Tries to set the thread handle for the current thread. Fails if a handle was +/// already set or if the thread ID of `thread` would change an already-set ID. +pub(crate) fn set_current(thread: Thread) -> Result<(), Thread> { + if CURRENT.get() != NONE { + return Err(thread); } - id::set(thread.id()); + match id::get() { + Some(id) if id == thread.id() => {} + None => id::set(thread.id()), + _ => return Err(thread), + } // Make sure that `crate::rt::thread_cleanup` will be run, which will // call `drop_current`. crate::sys::thread_local::guard::enable(); CURRENT.set(thread.into_raw().cast_mut()); + Ok(()) } /// Gets the id of the thread that invokes it. diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index d1d4eabb9bd..39753888509 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -519,9 +519,14 @@ impl Builder { let f = MaybeDangling::new(f); let main = move || { - // Immediately store the thread handle to avoid setting it or its ID - // twice, which would cause an abort. - set_current(their_thread.clone()); + if let Err(_thread) = set_current(their_thread.clone()) { + // Both the current thread handle and the ID should not be + // initialized yet. Since only the C runtime and some of our + // platform code run before this, this point shouldn't be + // reachable. Use an abort to save binary size (see #123356). + rtabort!("something here is badly broken!"); + } + if let Some(name) = their_thread.cname() { imp::Thread::set_name(name); } @@ -1159,9 +1164,6 @@ pub fn park_timeout(dur: Duration) { pub struct ThreadId(NonZero<u64>); impl ThreadId { - // DO NOT rely on this value. - const MAIN_THREAD: ThreadId = ThreadId(unsafe { NonZero::new_unchecked(1) }); - // Generate a new unique thread ID. pub(crate) fn new() -> ThreadId { #[cold] @@ -1173,7 +1175,7 @@ impl ThreadId { if #[cfg(target_has_atomic = "64")] { use crate::sync::atomic::AtomicU64; - static COUNTER: AtomicU64 = AtomicU64::new(1); + static COUNTER: AtomicU64 = AtomicU64::new(0); let mut last = COUNTER.load(Ordering::Relaxed); loop { @@ -1189,7 +1191,7 @@ impl ThreadId { } else { use crate::sync::{Mutex, PoisonError}; - static COUNTER: Mutex<u64> = Mutex::new(1); + static COUNTER: Mutex<u64> = Mutex::new(0); let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner); let Some(id) = counter.checked_add(1) else { @@ -1326,9 +1328,9 @@ impl Thread { Self::new_inner(id, ThreadName::Unnamed) } - // Used in runtime to construct main thread - pub(crate) fn new_main() -> Thread { - Self::new_inner(ThreadId::MAIN_THREAD, ThreadName::Main) + /// Constructs the thread handle for the main thread. + pub(crate) fn new_main(id: ThreadId) -> Thread { + Self::new_inner(id, ThreadName::Main) } fn new_inner(id: ThreadId, name: ThreadName) -> Thread { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 46026324d2f..5a476d5843b 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -3,11 +3,10 @@ #![feature(link_cfg)] #![feature(staged_api)] #![feature(strict_provenance)] -#![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] #![cfg_attr(not(target_env = "msvc"), feature(libc))] #![cfg_attr( all(target_family = "wasm", not(target_os = "emscripten")), - feature(link_llvm_intrinsics) + feature(simd_wasm64, wasm_exception_handling_intrinsics) )] #![allow(internal_features)] diff --git a/library/unwind/src/wasm.rs b/library/unwind/src/wasm.rs index f4ffac1ba16..2d36a8be004 100644 --- a/library/unwind/src/wasm.rs +++ b/library/unwind/src/wasm.rs @@ -40,29 +40,25 @@ pub unsafe fn _Unwind_DeleteException(exception: *mut _Unwind_Exception) { } pub unsafe fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code { - #[cfg(panic = "unwind")] - extern "C" { - /// LLVM lowers this intrinsic to the `throw` instruction. - // FIXME(coolreader18): move to stdarch - #[link_name = "llvm.wasm.throw"] - fn wasm_throw(tag: i32, ptr: *mut u8) -> !; - } - // The wasm `throw` instruction takes a "tag", which differentiates certain // types of exceptions from others. LLVM currently just identifies these // via integers, with 0 corresponding to C++ exceptions and 1 to C setjmp()/longjmp(). // Ideally, we'd be able to choose something unique for Rust, but for now, // we pretend to be C++ and implement the Itanium exception-handling ABI. cfg_if::cfg_if! { - // for now, unless we're -Zbuild-std with panic=unwind, never codegen a throw. + // panic=abort is default for wasm targets. Because an unknown instruction is a load-time + // error on wasm, instead of a runtime error like on traditional architectures, we never + // want to codegen a `throw` instruction, as that would break users using runtimes that + // don't yet support exceptions. The only time this first branch would be selected is if + // the user explicitly opts in to wasm exceptions, via -Zbuild-std with -Cpanic=unwind. if #[cfg(panic = "unwind")] { - wasm_throw(0, exception.cast()) + // corresponds with llvm::WebAssembly::Tag::CPP_EXCEPTION + // in llvm-project/llvm/include/llvm/CodeGen/WasmEHFuncInfo.h + const CPP_EXCEPTION_TAG: i32 = 0; + core::arch::wasm::throw::<CPP_EXCEPTION_TAG>(exception.cast()) } else { let _ = exception; - #[cfg(target_arch = "wasm32")] - core::arch::wasm32::unreachable(); - #[cfg(target_arch = "wasm64")] - core::arch::wasm64::unreachable(); + core::arch::wasm::unreachable() } } } diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index b9df7336cca..e9ec79e417b 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -14,6 +14,7 @@ use bootstrap::{ Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, find_recent_config_change_ids, human_readable_changes, t, }; +use build_helper::ci::CiEnv; fn main() { let args = env::args().skip(1).collect::<Vec<_>>(); @@ -54,9 +55,12 @@ fn main() { }; } - // check_version warnings are not printed during setup - let changelog_suggestion = - if matches!(config.cmd, Subcommand::Setup { .. }) { None } else { check_version(&config) }; + // check_version warnings are not printed during setup, or during CI + let changelog_suggestion = if matches!(config.cmd, Subcommand::Setup { .. }) || CiEnv::is_ci() { + None + } else { + check_version(&config) + }; // NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the // changelog warning, not the `x.py setup` message. diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 7671fc7e013..f419bebdc12 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -398,7 +398,14 @@ impl Step for RustAnalyzer { } macro_rules! tool_check_step { - ($name:ident, $path:literal, $($alias:literal, )* $source_type:path $(, $default:literal )?) => { + ( + $name:ident, + $display_name:literal, + $path:literal, + $($alias:literal, )* + $source_type:path + $(, $default:literal )? + ) => { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name { pub target: TargetSelection, @@ -441,7 +448,7 @@ macro_rules! tool_check_step { cargo.arg("--all-targets"); } - let _guard = builder.msg_check(&concat!(stringify!($name), " artifacts").to_lowercase(), target); + let _guard = builder.msg_check(&format!("{} artifacts", $display_name), target); run_cargo( builder, cargo, @@ -468,20 +475,30 @@ macro_rules! tool_check_step { }; } -tool_check_step!(Rustdoc, "src/tools/rustdoc", "src/librustdoc", SourceType::InTree); +tool_check_step!(Rustdoc, "rustdoc", "src/tools/rustdoc", "src/librustdoc", SourceType::InTree); // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead // of a submodule. Since the SourceType only drives the deny-warnings // behavior, treat it as in-tree so that any new warnings in clippy will be // rejected. -tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree); -tool_check_step!(Miri, "src/tools/miri", SourceType::InTree); -tool_check_step!(CargoMiri, "src/tools/miri/cargo-miri", SourceType::InTree); -tool_check_step!(Rls, "src/tools/rls", SourceType::InTree); -tool_check_step!(Rustfmt, "src/tools/rustfmt", SourceType::InTree); -tool_check_step!(MiroptTestTools, "src/tools/miropt-test-tools", SourceType::InTree); -tool_check_step!(TestFloatParse, "src/etc/test-float-parse", SourceType::InTree); - -tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree, false); +tool_check_step!(Clippy, "clippy", "src/tools/clippy", SourceType::InTree); +tool_check_step!(Miri, "miri", "src/tools/miri", SourceType::InTree); +tool_check_step!(CargoMiri, "cargo-miri", "src/tools/miri/cargo-miri", SourceType::InTree); +tool_check_step!(Rls, "rls", "src/tools/rls", SourceType::InTree); +tool_check_step!(Rustfmt, "rustfmt", "src/tools/rustfmt", SourceType::InTree); +tool_check_step!( + MiroptTestTools, + "miropt-test-tools", + "src/tools/miropt-test-tools", + SourceType::InTree +); +tool_check_step!( + TestFloatParse, + "test-float-parse", + "src/etc/test-float-parse", + SourceType::InTree +); + +tool_check_step!(Bootstrap, "bootstrap", "src/bootstrap", SourceType::InTree, false); /// Cargo's output path for the standard library in a given stage, compiled /// by a particular compiler for the specified target. diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 3d504c3771f..ca2b8742647 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -82,7 +82,7 @@ book!( EditionGuide, "src/doc/edition-guide", "edition-guide", &[], submodule; EmbeddedBook, "src/doc/embedded-book", "embedded-book", &[], submodule; Nomicon, "src/doc/nomicon", "nomicon", &[], submodule; - RustByExample, "src/doc/rust-by-example", "rust-by-example", &["ja"], submodule; + RustByExample, "src/doc/rust-by-example", "rust-by-example", &["ja", "zh"], submodule; RustdocBook, "src/doc/rustdoc", "rustdoc", &[]; StyleGuide, "src/doc/style-guide", "style-guide", &[]; ); @@ -718,6 +718,10 @@ fn doc_std( .arg("--target-dir") .arg(&*target_dir.to_string_lossy()) .arg("-Zskip-rustdoc-fingerprint") + .arg("-Zrustdoc-map") + .rustdocflag("--extern-html-root-url") + .rustdocflag("std_detect=https://docs.rs/std_detect/latest/") + .rustdocflag("--extern-html-root-takes-precedence") .rustdocflag("--resource-suffix") .rustdocflag(&builder.version); for arg in extra_args { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 4b403407a20..a2d40f6fbd8 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1228,6 +1228,9 @@ fn supported_sanitizers( "aarch64-unknown-linux-ohos" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) } + "loongarch64-unknown-linux-gnu" | "loongarch64-unknown-linux-musl" => { + common_libs("linux", "loongarch64", &["asan", "lsan", "msan", "tsan"]) + } "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]), diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index d32830c0a96..9ac0b0a01f7 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -72,36 +72,40 @@ impl<'a> Deref for Builder<'a> { } pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { - /// `PathBuf` when directories are created or to return a `Compiler` once - /// it's been assembled. + /// Result type of `Step::run`. type Output: Clone; - /// Whether this step is run by default as part of its respective phase. - /// `true` here can still be overwritten by `should_run` calling `default_condition`. + /// Whether this step is run by default as part of its respective phase, as defined by the `describe` + /// macro in [`Builder::get_step_descriptions`]. + /// + /// Note: Even if set to `true`, it can still be overridden with [`ShouldRun::default_condition`] + /// by `Step::should_run`. const DEFAULT: bool = false; /// If true, then this rule should be skipped if --target was specified, but --host was not const ONLY_HOSTS: bool = false; - /// Primary function to execute this rule. Can call `builder.ensure()` - /// with other steps to run those. + /// Primary function to implement `Step` logic. + /// + /// This function can be triggered in two ways: + /// 1. Directly from [`Builder::execute_cli`]. + /// 2. Indirectly by being called from other `Step`s using [`Builder::ensure`]. /// - /// This gets called twice during a normal `./x.py` execution: first - /// with `dry_run() == true`, and then for real. + /// When called with [`Builder::execute_cli`] (as done by `Build::build`), this function executed twice: + /// - First in "dry-run" mode to validate certain things (like cyclic Step invocations, + /// directory creation, etc) super quickly. + /// - Then it's called again to run the actual, very expensive process. + /// + /// When triggered indirectly from other `Step`s, it may still run twice (as dry-run and real mode) + /// depending on the `Step::run` implementation of the caller. fn run(self, builder: &Builder<'_>) -> Self::Output; - /// When bootstrap is passed a set of paths, this controls whether this rule - /// will execute. However, it does not get called in a "default" context - /// when we are not passed any paths; in that case, `make_run` is called - /// directly. + /// Determines if this `Step` should be run when given specific paths (e.g., `x build $path`). fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_>; - /// Builds up a "root" rule, either as a default rule or from a path passed - /// to us. - /// - /// When path is `None`, we are executing in a context where no paths were - /// passed. When `./x.py build` is run, for example, this rule could get - /// called if it is in the correct list below with a path of `None`. + /// Called directly by the bootstrap `Step` handler when not triggered indirectly by other `Step`s using [`Builder::ensure`]. + /// For example, `./x.py test bootstrap` runs this for `test::Bootstrap`. Similarly, `./x.py test` runs it for every step + /// that is listed by the `describe` macro in [`Builder::get_step_descriptions`]. fn make_run(_run: RunConfig<'_>) { // It is reasonable to not have an implementation of make_run for rules // who do not want to get called from the root context. This means that diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 3f025a198cb..f768470c4ff 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2748,13 +2748,18 @@ impl Config { } }; - // Look for a version to compare to based on the current commit. - // Only commits merged by bors will have CI artifacts. - let commit = get_closest_merge_commit(Some(&self.src), &self.git_config(), &[ + let files_to_track = &[ self.src.join("compiler"), self.src.join("library"), - ]) - .unwrap(); + self.src.join("src/version"), + self.src.join("src/stage0"), + self.src.join("src/ci/channel"), + ]; + + // Look for a version to compare to based on the current commit. + // Only commits merged by bors will have CI artifacts. + let commit = + get_closest_merge_commit(Some(&self.src), &self.git_config(), files_to_track).unwrap(); if commit.is_empty() { println!("ERROR: could not find commit hash for downloading rustc"); println!("HELP: maybe your repository history is too shallow?"); @@ -2780,7 +2785,7 @@ impl Config { let has_changes = !t!(helpers::git(Some(&self.src)) .args(["diff-index", "--quiet", &commit]) .arg("--") - .args([self.src.join("compiler"), self.src.join("library")]) + .args(files_to_track) .as_command_mut() .status()) .success(); diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 4b689aa3ecc..2611b6cf51b 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -10,6 +10,7 @@ use serde::Deserialize; use super::flags::Flags; use super::{ChangeIdWrapper, Config}; use crate::core::build_steps::clippy::get_clippy_rules_in_order; +use crate::core::build_steps::llvm; use crate::core::config::{LldMode, Target, TargetSelection, TomlConfig}; pub(crate) fn parse(config: &str) -> Config { @@ -21,9 +22,20 @@ pub(crate) fn parse(config: &str) -> Config { #[test] fn download_ci_llvm() { - assert!(parse("").llvm_from_ci); - assert!(parse("llvm.download-ci-llvm = true").llvm_from_ci); - assert!(!parse("llvm.download-ci-llvm = false").llvm_from_ci); + let config = parse(""); + let is_available = llvm::is_ci_llvm_available(&config, config.llvm_assertions); + if is_available { + assert!(config.llvm_from_ci); + } + + let config = parse("llvm.download-ci-llvm = true"); + let is_available = llvm::is_ci_llvm_available(&config, config.llvm_assertions); + if is_available { + assert!(config.llvm_from_ci); + } + + let config = parse("llvm.download-ci-llvm = false"); + assert!(!config.llvm_from_ci); let if_unchanged_config = parse("llvm.download-ci-llvm = \"if-unchanged\""); if if_unchanged_config.llvm_from_ci { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index ecb219ea33f..3924a6d714e 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1575,9 +1575,11 @@ Executed at: {executed_at}"#, fn rust_version(&self) -> String { let mut version = self.rust_info().version(self, &self.version); if let Some(ref s) = self.config.description { - version.push_str(" ("); - version.push_str(s); - version.push(')'); + if !s.is_empty() { + version.push_str(" ("); + version.push_str(s); + version.push(')'); + } } version } diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile index 865a9e32fa9..71eb72686b0 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -47,6 +47,7 @@ ENV RUST_CONFIGURE_ARGS \ --enable-extended \ --enable-full-tools \ --enable-profiler \ + --enable-sanitizers \ --disable-docs ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $TARGETS diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile index 62dbfaaa673..5081f25e567 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile @@ -29,6 +29,7 @@ ENV RUST_CONFIGURE_ARGS \ --enable-extended \ --enable-full-tools \ --enable-profiler \ + --enable-sanitizers \ --disable-docs \ --set target.loongarch64-unknown-linux-musl.crt-static=false \ --musl-root-loongarch64=/x-tools/loongarch64-unknown-linux-musl/loongarch64-unknown-linux-musl/sysroot/usr diff --git a/src/ci/docker/scripts/emscripten.sh b/src/ci/docker/scripts/emscripten.sh index 3f5e2c6ff1d..8b2b39ee162 100644 --- a/src/ci/docker/scripts/emscripten.sh +++ b/src/ci/docker/scripts/emscripten.sh @@ -20,5 +20,5 @@ exit 1 git clone https://github.com/emscripten-core/emsdk.git /emsdk-portable cd /emsdk-portable -hide_output ./emsdk install 2.0.5 -./emsdk activate 2.0.5 +hide_output ./emsdk install 3.1.68 +./emsdk activate 3.1.68 diff --git a/src/ci/scripts/setup-upstream-remote.sh b/src/ci/scripts/setup-upstream-remote.sh new file mode 100755 index 00000000000..52b4c98a890 --- /dev/null +++ b/src/ci/scripts/setup-upstream-remote.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# In CI environments, bootstrap is forced to use the remote upstream based +# on "git_repository" and "nightly_branch" values from src/stage0 file. +# This script configures the remote as it may not exist by default. + +set -euo pipefail +IFS=$'\n\t' + +ci_dir=$(cd $(dirname $0) && pwd)/.. +source "$ci_dir/shared.sh" + +git_repository=$(parse_stage0_file_by_key "git_repository") +nightly_branch=$(parse_stage0_file_by_key "nightly_branch") + +# Configure "rust-lang/rust" upstream remote only when it's not origin. +if [ -z "$(git config remote.origin.url | grep $git_repository)" ]; then + echo "Configuring https://github.com/$git_repository remote as upstream." + git remote add upstream "https://github.com/$git_repository" + REMOTE_NAME="upstream" +else + REMOTE_NAME="origin" +fi + +git fetch $REMOTE_NAME $nightly_branch diff --git a/src/ci/shared.sh b/src/ci/shared.sh index 2b0a10e4d08..1e6a008a5de 100644 --- a/src/ci/shared.sh +++ b/src/ci/shared.sh @@ -136,3 +136,15 @@ function releaseChannel { echo $RUST_CI_OVERRIDE_RELEASE_CHANNEL fi } + +# Parse values from src/stage0 file by key +function parse_stage0_file_by_key { + local key="$1" + local file="$ci_dir/../stage0" + local value=$(awk -F= '{a[$1]=$2} END {print(a["'$key'"])}' $file) + if [ -z "$value" ]; then + echo "ERROR: Key '$key' not found in '$file'." + exit 1 + fi + echo "$value" +} diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index fa73733360c..1ddad917b78 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -214,15 +214,15 @@ fn clean_generic_bound<'tcx>( ) -> Option<GenericBound> { Some(match *bound { hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), - hir::GenericBound::Trait(ref t, modifier) => { + hir::GenericBound::Trait(ref t) => { // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. - if modifier == hir::TraitBoundModifier::MaybeConst + if t.modifiers == hir::TraitBoundModifier::MaybeConst && cx.tcx.lang_items().destruct_trait() == Some(t.trait_ref.trait_def_id().unwrap()) { return None; } - GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier) + GenericBound::TraitBound(clean_poly_trait_ref(t, cx), t.modifiers) } hir::GenericBound::Use(args, ..) => { GenericBound::Use(args.iter().map(|arg| arg.name()).collect()) @@ -1833,7 +1833,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::Path(_) => clean_qpath(ty, cx), TyKind::TraitObject(bounds, lifetime, _) => { - let bounds = bounds.iter().map(|(bound, _)| clean_poly_trait_ref(bound, cx)).collect(); + let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect(); let lifetime = if !lifetime.is_elided() { Some(clean_lifetime(lifetime, cx)) } else { None }; DynTrait(bounds, lifetime) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index d5bc2a93fa8..7b2a5eb3d63 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -643,8 +643,7 @@ fn run_test( } else { cmd = Command::new(&output_file); if doctest.is_multiple_tests { - cmd.arg("*doctest-bin-path"); - cmd.arg(&output_file); + cmd.env("RUSTDOC_DOCTEST_BIN_PATH", &output_file); } } if let Some(run_directory) = &rustdoc_options.test_run_directory { diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 942ec8d9936..093755103f3 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -112,8 +112,7 @@ mod __doctest_mod {{ use std::path::PathBuf; pub static BINARY_PATH: OnceLock<PathBuf> = OnceLock::new(); - pub const RUN_OPTION: &str = \"*doctest-inner-test\"; - pub const BIN_OPTION: &str = \"*doctest-bin-path\"; + pub const RUN_OPTION: &str = \"RUSTDOC_DOCTEST_RUN_NB_TEST\"; #[allow(unused)] pub fn doctest_path() -> Option<&'static PathBuf> {{ @@ -123,8 +122,8 @@ mod __doctest_mod {{ #[allow(unused)] pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> Result<(), String> {{ let out = std::process::Command::new(bin) - .arg(self::RUN_OPTION) - .arg(test_nb.to_string()) + .env(self::RUN_OPTION, test_nb.to_string()) + .args(std::env::args().skip(1).collect::<Vec<_>>()) .output() .expect(\"failed to run command\"); if !out.status.success() {{ @@ -138,36 +137,27 @@ mod __doctest_mod {{ #[rustc_main] fn main() -> std::process::ExitCode {{ const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}]; -let bin_marker = std::ffi::OsStr::new(__doctest_mod::BIN_OPTION); let test_marker = std::ffi::OsStr::new(__doctest_mod::RUN_OPTION); let test_args = &[{test_args}]; +const ENV_BIN: &'static str = \"RUSTDOC_DOCTEST_BIN_PATH\"; -let mut args = std::env::args_os().skip(1); -while let Some(arg) = args.next() {{ - if arg == bin_marker {{ - let Some(binary) = args.next() else {{ - panic!(\"missing argument after `{{}}`\", __doctest_mod::BIN_OPTION); - }}; - if crate::__doctest_mod::BINARY_PATH.set(binary.into()).is_err() {{ - panic!(\"`{{}}` option was used more than once\", bin_marker.to_string_lossy()); - }} - return std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None)); - }} else if arg == test_marker {{ - let Some(nb_test) = args.next() else {{ - panic!(\"missing argument after `{{}}`\", __doctest_mod::RUN_OPTION); - }}; - if let Some(nb_test) = nb_test.to_str().and_then(|nb| nb.parse::<usize>().ok()) {{ - if let Some(test) = TESTS.get(nb_test) {{ - if let test::StaticTestFn(f) = test.testfn {{ - return std::process::Termination::report(f()); - }} +if let Ok(binary) = std::env::var(ENV_BIN) {{ + let _ = crate::__doctest_mod::BINARY_PATH.set(binary.into()); + unsafe {{ std::env::remove_var(ENV_BIN); }} + return std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None)); +}} else if let Ok(nb_test) = std::env::var(__doctest_mod::RUN_OPTION) {{ + if let Ok(nb_test) = nb_test.parse::<usize>() {{ + if let Some(test) = TESTS.get(nb_test) {{ + if let test::StaticTestFn(f) = test.testfn {{ + return std::process::Termination::report(f()); }} }} - panic!(\"Unexpected value after `{{}}`\", __doctest_mod::RUN_OPTION); }} + panic!(\"Unexpected value for `{{}}`\", __doctest_mod::RUN_OPTION); }} -eprintln!(\"WARNING: No argument provided so doctests will be run in the same process\"); +eprintln!(\"WARNING: No rustdoc doctest environment variable provided so doctests will be run in \ +the same process\"); std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None)) }}", nb_tests = self.nb_tests, diff --git a/src/llvm-project b/src/llvm-project -Subproject dd46457da782554454106d48ecd4f6b4c2f9af7 +Subproject 3a17f74904a74565c54cfac0d67026362d03869 diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs index 1e28d552fe6..2aad5650fa8 100644 --- a/src/tools/build_helper/src/git.rs +++ b/src/tools/build_helper/src/git.rs @@ -1,6 +1,8 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; +use crate::ci::CiEnv; + pub struct GitConfig<'a> { pub git_repository: &'a str, pub nightly_branch: &'a str, @@ -114,8 +116,8 @@ fn git_upstream_merge_base( /// Searches for the nearest merge commit in the repository that also exists upstream. /// -/// If it fails to find the upstream remote, it then looks for the most recent commit made -/// by the merge bot by matching the author's email address with the merge bot's email. +/// It looks for the most recent commit made by the merge bot by matching the author's email +/// address with the merge bot's email. pub fn get_closest_merge_commit( git_dir: Option<&Path>, config: &GitConfig<'_>, @@ -127,7 +129,15 @@ pub fn get_closest_merge_commit( git.current_dir(git_dir); } - let merge_base = git_upstream_merge_base(config, git_dir).unwrap_or_else(|_| "HEAD".into()); + let merge_base = { + if CiEnv::is_ci() { + git_upstream_merge_base(config, git_dir).unwrap() + } else { + // For non-CI environments, ignore rust-lang/rust upstream as it usually gets + // outdated very quickly. + "HEAD".to_string() + } + }; git.args([ "rev-list", diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 5f349d78053..590d9afd1b4 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -242,7 +242,8 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds bounds .iter() .filter_map(|bound| { - if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound + if let GenericBound::Trait(poly_trait) = bound + && let TraitBoundModifier::None = poly_trait.modifiers && let [.., path] = poly_trait.trait_ref.path.segments && poly_trait.bound_generic_params.is_empty() && let Some(trait_def_id) = path.res.opt_def_id() @@ -307,7 +308,8 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) { // This involves some extra logic when generic arguments are present, since // simply comparing trait `DefId`s won't be enough. We also need to compare the generics. for (index, bound) in bounds.iter().enumerate() { - if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound + if let GenericBound::Trait(poly_trait) = bound + && let TraitBoundModifier::None = poly_trait.modifiers && let [.., path] = poly_trait.trait_ref.path.segments && let implied_args = path.args.map_or([].as_slice(), |a| a.args) && let implied_constraints = path.args.map_or([].as_slice(), |a| a.constraints) diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 73ebe6aec15..96550c4d1cb 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::ty::is_copy; use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::HirId; @@ -133,7 +133,7 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) { .index_use .iter() .map(|(index, _)| *index) - .collect::<FxHashSet<_>>(); + .collect::<FxIndexSet<_>>(); let value_name = |index| format!("{}_{index}", slice.ident.name); diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 311bbce14bd..035ee40348c 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -310,7 +310,7 @@ fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<& if let ty::Alias(_, alias_ty) = ty.kind() && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir().get_if_local(alias_ty.def_id) && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin - && let [GenericBound::Trait(trait_ref, _)] = &opaque.bounds + && let [GenericBound::Trait(trait_ref)] = &opaque.bounds && let Some(segment) = trait_ref.trait_ref.path.segments.last() && let Some(generic_args) = segment.args && let [constraint] = generic_args.constraints diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 012aa689d4b..6ee064a6124 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -26,8 +26,6 @@ unused_qualifications, rustc::internal )] -// Disable this rustc lint for now, as it was also done in rustc -#![allow(rustc::potential_query_instability)] // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 7c3ef98fd74..a7c48eb216a 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::trait_ref_of_method; use itertools::Itertools; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; @@ -163,7 +163,7 @@ fn check_fn_inner<'tcx>( if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) { return; } - if let GenericBound::Trait(ref trait_ref, _) = *bound { + if let GenericBound::Trait(ref trait_ref) = *bound { let params = &trait_ref .trait_ref .path @@ -311,7 +311,7 @@ fn could_use_elision<'tcx>( Some((elidable_lts, usages)) } -fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<LocalDefId> { +fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefId> { named_generics .iter() .filter_map(|par| { @@ -438,7 +438,7 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> { if !lt.is_elided() { self.unelided_trait_object_lifetime = true; } - for (bound, _) in bounds { + for bound in bounds { self.visit_poly_trait_ref(bound); } }, @@ -497,7 +497,7 @@ struct Usage { struct LifetimeChecker<'cx, 'tcx, F> { cx: &'cx LateContext<'tcx>, - map: FxHashMap<LocalDefId, Vec<Usage>>, + map: FxIndexMap<LocalDefId, Vec<Usage>>, where_predicate_depth: usize, generic_args_depth: usize, phantom: std::marker::PhantomData<F>, @@ -619,7 +619,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' fn report_elidable_impl_lifetimes<'tcx>( cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>, - map: &FxHashMap<LocalDefId, Vec<Usage>>, + map: &FxIndexMap<LocalDefId, Vec<Usage>>, ) { let single_usages = map .iter() diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 745f070a577..214b8b0f379 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg}; use rustc_ast::ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -39,7 +39,7 @@ pub(super) fn check<'tcx>( var: canonical_id, indexed_mut: FxHashSet::default(), indexed_indirectly: FxHashMap::default(), - indexed_directly: FxHashMap::default(), + indexed_directly: FxIndexMap::default(), referenced: FxHashSet::default(), nonindex: false, prefer_mutable: false, @@ -229,7 +229,7 @@ struct VarVisitor<'a, 'tcx> { indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>, /// subset of `indexed` of vars that are indexed directly: `v[i]` /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]` - indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>, + indexed_directly: FxIndexMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>, /// Any names that are used outside an index operation. /// Used to detect things like `&mut vec` used together with `vec[i]` referenced: FxHashSet<Symbol>, diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 81115cffdca..67255c1af79 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -107,7 +107,7 @@ fn future_trait_ref<'tcx>( ) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> { if let TyKind::OpaqueDef(opaque, bounds) = ty.kind && let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { - if let GenericBound::Trait(poly, _) = bound { + if let GenericBound::Trait(poly) = bound { Some(&poly.trait_ref) } else { None diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 7372f52e1e5..2ce6a8a85a5 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -7,6 +7,7 @@ use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{get_attr, is_lint_allowed}; use itertools::Itertools; use rustc_ast::Mutability; +use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; @@ -475,19 +476,19 @@ impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> { struct ArmSigDropHelper<'a, 'tcx> { sig_drop_checker: SigDropChecker<'a, 'tcx>, - found_sig_drop_spans: FxHashSet<Span>, + found_sig_drop_spans: FxIndexSet<Span>, } impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> ArmSigDropHelper<'a, 'tcx> { ArmSigDropHelper { sig_drop_checker: SigDropChecker::new(cx), - found_sig_drop_spans: FxHashSet::<Span>::default(), + found_sig_drop_spans: FxIndexSet::<Span>::default(), } } } -fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxHashSet<Span> { +fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxIndexSet<Span> { let mut helper = ArmSigDropHelper::new(cx); for arm in arms { helper.visit_expr(arm); diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index b40d7eba15e..c56a4014b34 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -8,7 +8,7 @@ use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, hash_expr, higher}; use rustc_ast::{LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; -use rustc_data_structures::unhash::UnhashMap; +use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -226,7 +226,7 @@ fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> { } /// Checks if the expression is an index into a slice and adds it to `indexes` -fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) { +fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap<u64, Vec<IndexEntry<'hir>>>) { if let ExprKind::Index(slice, index_lit, _) = expr.kind && cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice() && let Some(index) = upper_index_expr(index_lit) @@ -274,7 +274,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Unh } /// Checks if the expression is an `assert!` expression and adds it to `asserts` -fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) { +fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap<u64, Vec<IndexEntry<'hir>>>) { if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) { let hash = hash_expr(cx, slice); let indexes = map.entry(hash).or_default(); @@ -311,7 +311,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un /// Inspects indexes and reports lints. /// /// Called at the end of this lint after all indexing and `assert!` expressions have been collected. -fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>>) { +fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>>>) { for bucket in map.values() { for entry in bucket { let Some(full_span) = entry @@ -403,7 +403,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>> impl LateLintPass<'_> for MissingAssertsForIndexing { fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { - let mut map = UnhashMap::default(); + let mut map = UnindexMap::default(); for_each_expr_without_closures(body.value, |expr| { check_index(cx, expr, &mut map); diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index e9c5f64a255..676d608eb31 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LOCAL_CRATE; @@ -87,7 +87,7 @@ impl EarlyLintPass for ModStyle { // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives // `[path, to]` but not foo - let mut folder_segments = FxHashSet::default(); + let mut folder_segments = FxIndexSet::default(); // `mod_folders` is all the unique folder names that contain a mod.rs file let mut mod_folders = FxHashSet::default(); // `file_map` maps file names to the full path including the file name @@ -144,7 +144,7 @@ impl EarlyLintPass for ModStyle { /// is `mod.rs` we add it's parent folder to `mod_folders`. fn process_paths_for_mod_files<'a>( path: &'a Path, - folder_segments: &mut FxHashSet<&'a OsStr>, + folder_segments: &mut FxIndexSet<&'a OsStr>, mod_folders: &mut FxHashSet<&'a OsStr>, ) { let mut comp = path.components().rev().peekable(); diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs index bb44ff37b20..a56024f08d5 100644 --- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs +++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs @@ -40,7 +40,6 @@ struct Bound<'tcx> { ident: Ident, trait_bound: &'tcx PolyTraitRef<'tcx>, - modifier: TraitBoundModifier, predicate_pos: usize, bound_pos: usize, @@ -65,11 +64,10 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item .iter() .enumerate() .filter_map(move |(bound_pos, bound)| match bound { - &GenericBound::Trait(ref trait_bound, modifier) => Some(Bound { + &GenericBound::Trait(ref trait_bound) => Some(Bound { param, ident, trait_bound, - modifier, predicate_pos, bound_pos, }), @@ -120,13 +118,13 @@ impl LateLintPass<'_> for NeedlessMaybeSized { let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics) .filter(|bound| { bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait) - && bound.modifier == TraitBoundModifier::Maybe + && bound.trait_bound.modifiers == TraitBoundModifier::Maybe }) .map(|bound| (bound.param, bound)) .collect(); for bound in type_param_bounds(generics) { - if bound.modifier == TraitBoundModifier::None + if bound.trait_bound.modifiers == TraitBoundModifier::None && let Some(sized_bound) = maybe_sized_params.get(&bound.param) && let Some(path) = path_to_sized_bound(cx, bound.trait_bound) { diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index c2facb2fcf6..5c631a176c4 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr; use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self}; use core::ops::ControlFlow; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -101,7 +101,7 @@ fn check_closures<'tcx>( ctx: &mut MutablyUsedVariablesCtxt<'tcx>, cx: &LateContext<'tcx>, checked_closures: &mut FxHashSet<LocalDefId>, - closures: FxHashSet<LocalDefId>, + closures: FxIndexSet<LocalDefId>, ) { let hir = cx.tcx.hir(); for closure in closures { @@ -196,7 +196,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { prev_bind: None, prev_move_to_closure: HirIdSet::default(), aliases: HirIdMap::default(), - async_closures: FxHashSet::default(), + async_closures: FxIndexSet::default(), tcx: cx.tcx, }; euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx) @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // We retrieve all the closures declared in the function because they will not be found // by `euv::Delegate`. - let mut closures: FxHashSet<LocalDefId> = FxHashSet::default(); + let mut closures: FxIndexSet<LocalDefId> = FxIndexSet::default(); for_each_expr(cx, body, |expr| { if let ExprKind::Closure(closure) = expr.kind { closures.insert(closure.def_id); @@ -307,7 +307,7 @@ struct MutablyUsedVariablesCtxt<'tcx> { /// use of a variable. prev_move_to_closure: HirIdSet, aliases: HirIdMap<HirId>, - async_closures: FxHashSet<LocalDefId>, + async_closures: FxIndexSet<LocalDefId>, tcx: TyCtxt<'tcx>, } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index a3145c4647c..6cba560393d 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -6,9 +6,9 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core}; use itertools::Itertools; +use rustc_data_structures::fx::FxIndexSet; use rustc_hir::intravisit::{Visitor, walk_expr}; -use crate::FxHashSet; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -334,7 +334,7 @@ struct IndexBinding<'a, 'tcx> { impl<'tcx> IndexBinding<'_, 'tcx> { fn snippet_index_bindings(&mut self, exprs: &[&'tcx Expr<'tcx>]) -> String { - let mut bindings = FxHashSet::default(); + let mut bindings = FxIndexSet::default(); for expr in exprs { bindings.insert(self.snippet_index_binding(expr)); } diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 00277593622..38befdee574 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -5,7 +5,7 @@ use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro}; use core::hash::{Hash, Hasher}; use itertools::Itertools; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, IndexEntry}; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -16,7 +16,6 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Span}; -use std::collections::hash_map::Entry; declare_clippy_lint! { /// ### What it does @@ -183,7 +182,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { // Iterate the bounds and add them to our seen hash // If we haven't yet seen it, add it to the fixed traits - for (bound, _) in bounds { + for bound in bounds { let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; }; @@ -198,9 +197,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { // If the number of unique traits isn't the same as the number of traits in the bounds, // there must be 1 or more duplicates if bounds.len() != unique_traits.len() { - let mut bounds_span = bounds[0].0.span; + let mut bounds_span = bounds[0].span; - for (bound, _) in bounds.iter().skip(1) { + for bound in bounds.iter().skip(1) { bounds_span = bounds_span.to(bound.span); } @@ -230,7 +229,8 @@ impl TraitBounds { /// this MSRV? See <https://github.com/rust-lang/rust-clippy/issues/8772> for details. fn cannot_combine_maybe_bound(&self, cx: &LateContext<'_>, bound: &GenericBound<'_>) -> bool { if !self.msrv.meets(msrvs::MAYBE_BOUND_IN_WHERE) - && let GenericBound::Trait(tr, TraitBoundModifier::Maybe) = bound + && let GenericBound::Trait(tr) = bound + && let TraitBoundModifier::Maybe = tr.modifiers { cx.tcx.lang_items().get(LangItem::Sized) == tr.trait_ref.path.res.opt_def_id() } else { @@ -376,11 +376,11 @@ impl Default for ComparableTraitRef { } fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> { - if let GenericBound::Trait(t, tbm) = bound { + if let GenericBound::Trait(t) = bound { let trait_path = t.trait_ref.path; let trait_span = { let path_span = trait_path.span; - if let TraitBoundModifier::Maybe = tbm { + if let TraitBoundModifier::Maybe = t.modifiers { path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?` } else { path_span @@ -427,11 +427,11 @@ fn rollup_traits( bounds: &[GenericBound<'_>], msg: &'static str, ) -> Vec<(ComparableTraitRef, Span)> { - let mut map = FxHashMap::default(); + let mut map = FxIndexMap::default(); let mut repeated_res = false; let only_comparable_trait_refs = |bound: &GenericBound<'_>| { - if let GenericBound::Trait(t, _) = bound { + if let GenericBound::Trait(t) = bound { Some((into_comparable_trait_ref(&t.trait_ref), t.span)) } else { None @@ -442,8 +442,8 @@ fn rollup_traits( for bound in bounds.iter().filter_map(only_comparable_trait_refs) { let (comparable_bound, span_direct) = bound; match map.entry(comparable_bound) { - Entry::Occupied(_) => repeated_res = true, - Entry::Vacant(e) => { + IndexEntry::Occupied(_) => repeated_res = true, + IndexEntry::Vacant(e) => { e.insert((span_direct, i)); i += 1; }, diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index 2fcfc71a8c7..eb7ffbbe360 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -82,7 +82,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Returns true if given type is `Any` trait. fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { if let TyKind::TraitObject(traits, ..) = t.kind { - return traits.iter().any(|(bound, _)| { + return traits.iter().any(|bound| { if let Some(trait_did) = bound.trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::Any, trait_did) { diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs index 0b64fddb447..b89bd6a8d05 100644 --- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs @@ -55,7 +55,6 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { TyKind::TraitObject(param_bounds, _, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { bound - .0 .bound_generic_params .iter() .any(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 68f74e52ed7..0be6dc15a8e 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -753,6 +753,9 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool { (Ref(ll, l), Ref(rl, r)) => { both(ll.as_ref(), rl.as_ref(), |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty) }, + (PinnedRef(ll, l), PinnedRef(rl, r)) => { + both(ll.as_ref(), rl.as_ref(), |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty) + }, (BareFn(l), BareFn(r)) => { l.safety == r.safety && eq_ext(&l.ext, &r.ext) @@ -783,7 +786,8 @@ pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool { } pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool { - eq_path(&l.trait_ref.path, &r.trait_ref.path) + l.modifiers == r.modifiers + && eq_path(&l.trait_ref.path, &r.trait_ref.path) && over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { eq_generic_param(l, r) }) @@ -817,7 +821,7 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool { pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool { use GenericBound::*; match (l, r) { - (Trait(ptr1, tbm1), Trait(ptr2, tbm2)) => tbm1 == tbm2 && eq_poly_ref_trait(ptr1, ptr2), + (Trait(ptr1), Trait(ptr2)) => eq_poly_ref_trait(ptr1, ptr2), (Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident), _ => false, } diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index edfffe8fcfe..355f2bc7736 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -3,7 +3,7 @@ #![allow(dead_code, incomplete_features)] #![warn(clippy::doc_markdown)] -#![feature(custom_inner_attributes, generic_const_exprs, const_option)] +#![feature(custom_inner_attributes, generic_const_exprs)] #![rustfmt::skip] /// The `foo_bar` function does _nothing_. See also `foo::bar`. (note the dot there) diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index 3c0f6913e32..9ced2677622 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -3,7 +3,7 @@ #![allow(dead_code, incomplete_features)] #![warn(clippy::doc_markdown)] -#![feature(custom_inner_attributes, generic_const_exprs, const_option)] +#![feature(custom_inner_attributes, generic_const_exprs)] #![rustfmt::skip] /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index a5418ad8384..1ee00a3a4e8 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -759,14 +759,8 @@ pub fn output_testname_unique( /// test/revision should reside. Example: /// /path/to/build/host-triple/test/ui/relative/testname.revision.mode/ pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { - // In run-make tests, constructing a relative path + unique testname causes a double layering - // since revisions are not supported, causing unnecessary nesting. - if config.mode == Mode::RunMake { - output_relative_path(config, &testpaths.relative_dir) - } else { - output_relative_path(config, &testpaths.relative_dir) - .join(output_testname_unique(config, testpaths, revision)) - } + output_relative_path(config, &testpaths.relative_dir) + .join(output_testname_unique(config, testpaths, revision)) } /// Absolute path to the base filename used as output for the given diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs new file mode 100644 index 00000000000..b605bc813f1 --- /dev/null +++ b/src/tools/compiletest/src/debuggers.rs @@ -0,0 +1,272 @@ +use std::env; +use std::ffi::OsString; +use std::path::PathBuf; +use std::process::Command; +use std::sync::Arc; + +use crate::common::{Config, Debugger}; + +pub(crate) fn configure_cdb(config: &Config) -> Option<Arc<Config>> { + config.cdb.as_ref()?; + + Some(Arc::new(Config { debugger: Some(Debugger::Cdb), ..config.clone() })) +} + +pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> { + config.gdb_version?; + + if config.matches_env("msvc") { + return None; + } + + if config.remote_test_client.is_some() && !config.target.contains("android") { + println!( + "WARNING: debuginfo tests are not available when \ + testing with remote" + ); + return None; + } + + if config.target.contains("android") { + println!( + "{} debug-info test uses tcp 5039 port.\ + please reserve it", + config.target + ); + + // android debug-info test uses remote debugger so, we test 1 thread + // at once as they're all sharing the same TCP port to communicate + // over. + // + // we should figure out how to lift this restriction! (run them all + // on different ports allocated dynamically). + env::set_var("RUST_TEST_THREADS", "1"); + } + + Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() })) +} + +pub(crate) fn configure_lldb(config: &Config) -> Option<Arc<Config>> { + config.lldb_python_dir.as_ref()?; + + if let Some(350) = config.lldb_version { + println!( + "WARNING: The used version of LLDB (350) has a \ + known issue that breaks debuginfo tests. See \ + issue #32520 for more information. Skipping all \ + LLDB-based tests!", + ); + return None; + } + + Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() })) +} + +/// Returns `true` if the given target is an Android target for the +/// purposes of GDB testing. +pub(crate) fn is_android_gdb_target(target: &str) -> bool { + matches!( + &target[..], + "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" + ) +} + +/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing. +fn is_pc_windows_msvc_target(target: &str) -> bool { + target.ends_with("-pc-windows-msvc") +} + +fn find_cdb(target: &str) -> Option<OsString> { + if !(cfg!(windows) && is_pc_windows_msvc_target(target)) { + return None; + } + + let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?; + let cdb_arch = if cfg!(target_arch = "x86") { + "x86" + } else if cfg!(target_arch = "x86_64") { + "x64" + } else if cfg!(target_arch = "aarch64") { + "arm64" + } else if cfg!(target_arch = "arm") { + "arm" + } else { + return None; // No compatible CDB.exe in the Windows 10 SDK + }; + + let mut path = PathBuf::new(); + path.push(pf86); + path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too? + path.push(cdb_arch); + path.push(r"cdb.exe"); + + if !path.exists() { + return None; + } + + Some(path.into_os_string()) +} + +/// Returns Path to CDB +pub(crate) fn analyze_cdb( + cdb: Option<String>, + target: &str, +) -> (Option<OsString>, Option<[u16; 4]>) { + let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target)); + + let mut version = None; + if let Some(cdb) = cdb.as_ref() { + if let Ok(output) = Command::new(cdb).arg("/version").output() { + if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() { + version = extract_cdb_version(&first_line); + } + } + } + + (cdb, version) +} + +pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> { + // Example full_version_line: "cdb version 10.0.18362.1" + let version = full_version_line.rsplit(' ').next()?; + let mut components = version.split('.'); + let major: u16 = components.next().unwrap().parse().unwrap(); + let minor: u16 = components.next().unwrap().parse().unwrap(); + let patch: u16 = components.next().unwrap_or("0").parse().unwrap(); + let build: u16 = components.next().unwrap_or("0").parse().unwrap(); + Some([major, minor, patch, build]) +} + +/// Returns (Path to GDB, GDB Version) +pub(crate) fn analyze_gdb( + gdb: Option<String>, + target: &str, + android_cross_path: &PathBuf, +) -> (Option<String>, Option<u32>) { + #[cfg(not(windows))] + const GDB_FALLBACK: &str = "gdb"; + #[cfg(windows)] + const GDB_FALLBACK: &str = "gdb.exe"; + + let fallback_gdb = || { + if is_android_gdb_target(target) { + let mut gdb_path = match android_cross_path.to_str() { + Some(x) => x.to_owned(), + None => panic!("cannot find android cross path"), + }; + gdb_path.push_str("/bin/gdb"); + gdb_path + } else { + GDB_FALLBACK.to_owned() + } + }; + + let gdb = match gdb { + None => fallback_gdb(), + Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb + Some(ref s) => s.to_owned(), + }; + + let mut version_line = None; + if let Ok(output) = Command::new(&gdb).arg("--version").output() { + if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() { + version_line = Some(first_line.to_string()); + } + } + + let version = match version_line { + Some(line) => extract_gdb_version(&line), + None => return (None, None), + }; + + (Some(gdb), version) +} + +pub(crate) fn extract_gdb_version(full_version_line: &str) -> Option<u32> { + let full_version_line = full_version_line.trim(); + + // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both + // of the ? sections being optional + + // We will parse up to 3 digits for each component, ignoring the date + + // We skip text in parentheses. This avoids accidentally parsing + // the openSUSE version, which looks like: + // GNU gdb (GDB; openSUSE Leap 15.0) 8.1 + // This particular form is documented in the GNU coding standards: + // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion + + let unbracketed_part = full_version_line.split('[').next().unwrap(); + let mut splits = unbracketed_part.trim_end().rsplit(' '); + let version_string = splits.next().unwrap(); + + let mut splits = version_string.split('.'); + let major = splits.next().unwrap(); + let minor = splits.next().unwrap(); + let patch = splits.next(); + + let major: u32 = major.parse().unwrap(); + let (minor, patch): (u32, u32) = match minor.find(not_a_digit) { + None => { + let minor = minor.parse().unwrap(); + let patch: u32 = match patch { + Some(patch) => match patch.find(not_a_digit) { + None => patch.parse().unwrap(), + Some(idx) if idx > 3 => 0, + Some(idx) => patch[..idx].parse().unwrap(), + }, + None => 0, + }; + (minor, patch) + } + // There is no patch version after minor-date (e.g. "4-2012"). + Some(idx) => { + let minor = minor[..idx].parse().unwrap(); + (minor, 0) + } + }; + + Some(((major * 1000) + minor) * 1000 + patch) +} + +/// Returns LLDB version +pub(crate) fn extract_lldb_version(full_version_line: &str) -> Option<u32> { + // Extract the major LLDB version from the given version string. + // LLDB version strings are different for Apple and non-Apple platforms. + // The Apple variant looks like this: + // + // LLDB-179.5 (older versions) + // lldb-300.2.51 (new versions) + // + // We are only interested in the major version number, so this function + // will return `Some(179)` and `Some(300)` respectively. + // + // Upstream versions look like: + // lldb version 6.0.1 + // + // There doesn't seem to be a way to correlate the Apple version + // with the upstream version, and since the tests were originally + // written against Apple versions, we make a fake Apple version by + // multiplying the first number by 100. This is a hack. + + let full_version_line = full_version_line.trim(); + + if let Some(apple_ver) = + full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-")) + { + if let Some(idx) = apple_ver.find(not_a_digit) { + let version: u32 = apple_ver[..idx].parse().unwrap(); + return Some(version); + } + } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") { + if let Some(idx) = lldb_ver.find(not_a_digit) { + let version: u32 = lldb_ver[..idx].parse().ok()?; + return Some(version * 100); + } + } + None +} + +fn not_a_digit(c: char) -> bool { + !c.is_ascii_digit() +} diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 63d05886166..099e620ffe0 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -9,11 +9,11 @@ use std::process::Command; use tracing::*; use crate::common::{Config, Debugger, FailMode, Mode, PassMode}; +use crate::debuggers::{extract_cdb_version, extract_gdb_version}; use crate::header::auxiliary::{AuxProps, parse_and_update_aux}; use crate::header::cfg::{MatchOutcome, parse_cfg_name_directive}; use crate::header::needs::CachedNeedsConditions; use crate::util::static_regex; -use crate::{extract_cdb_version, extract_gdb_version}; pub(crate) mod auxiliary; mod cfg; @@ -57,9 +57,9 @@ impl EarlyProps { &mut poisoned, testfile, rdr, - &mut |HeaderLine { directive: ln, .. }| { + &mut |DirectiveLine { directive: ln, .. }| { parse_and_update_aux(config, ln, &mut props.aux); - config.parse_and_update_revisions(ln, &mut props.revisions); + config.parse_and_update_revisions(testfile, ln, &mut props.revisions); }, ); @@ -344,7 +344,7 @@ impl TestProps { &mut poisoned, testfile, file, - &mut |HeaderLine { header_revision, directive: ln, .. }| { + &mut |DirectiveLine { header_revision, directive: ln, .. }| { if header_revision.is_some() && header_revision != test_revision { return; } @@ -391,7 +391,7 @@ impl TestProps { has_edition = true; } - config.parse_and_update_revisions(ln, &mut self.revisions); + config.parse_and_update_revisions(testfile, ln, &mut self.revisions); if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS) { self.run_flags.extend(split_flags(&flags)); @@ -680,7 +680,7 @@ impl TestProps { /// Extract an `(Option<line_revision>, directive)` directive from a line if comment is present. /// -/// See [`HeaderLine`] for a diagram. +/// See [`DirectiveLine`] for a diagram. pub fn line_directive<'line>( comment: &str, original_line: &'line str, @@ -738,17 +738,13 @@ const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] = /// ```text /// //@ compile-flags: -O /// ^^^^^^^^^^^^^^^^^ directive -/// ^^^^^^^^^^^^^^^^^^^^^ original_line /// /// //@ [foo] compile-flags: -O /// ^^^ header_revision /// ^^^^^^^^^^^^^^^^^ directive -/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ original_line /// ``` -struct HeaderLine<'ln> { +struct DirectiveLine<'ln> { line_number: usize, - /// Raw line from the test file, including comment prefix and any revision. - original_line: &'ln str, /// Some header directives start with a revision name in square brackets /// (e.g. `[foo]`), and only apply to that revision of the test. /// If present, this field contains the revision name (e.g. `foo`). @@ -803,7 +799,7 @@ fn iter_header( poisoned: &mut bool, testfile: &Path, rdr: impl Read, - it: &mut dyn FnMut(HeaderLine<'_>), + it: &mut dyn FnMut(DirectiveLine<'_>), ) { if testfile.is_dir() { return; @@ -824,7 +820,7 @@ fn iter_header( ]; // Process the extra implied directives, with a dummy line number of 0. for directive in extra_directives { - it(HeaderLine { line_number: 0, original_line: "", header_revision: None, directive }); + it(DirectiveLine { line_number: 0, header_revision: None, directive }); } } @@ -841,11 +837,6 @@ fn iter_header( if rdr.read_line(&mut ln).unwrap() == 0 { break; } - - // Assume that any directives will be found before the first - // module or function. This doesn't seem to be an optimization - // with a warm page cache. Maybe with a cold one. - let original_line = &ln; let ln = ln.trim(); // Assume that any directives will be found before the first module or function. This @@ -897,9 +888,8 @@ fn iter_header( } } - it(HeaderLine { + it(DirectiveLine { line_number, - original_line, header_revision, directive: non_revisioned_directive_line, }); @@ -907,12 +897,21 @@ fn iter_header( } impl Config { - fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) { + fn parse_and_update_revisions(&self, testfile: &Path, line: &str, existing: &mut Vec<String>) { if let Some(raw) = self.parse_name_value_directive(line, "revisions") { + if self.mode == Mode::RunMake { + panic!("`run-make` tests do not support revisions: {}", testfile.display()); + } + let mut duplicates: HashSet<_> = existing.iter().cloned().collect(); for revision in raw.split_whitespace().map(|r| r.to_string()) { if !duplicates.insert(revision.clone()) { - panic!("Duplicate revision: `{}` in line `{}`", revision, raw); + panic!( + "duplicate revision: `{}` in line `{}`: {}", + revision, + raw, + testfile.display() + ); } existing.push(revision); } @@ -1286,13 +1285,14 @@ pub fn make_test_description<R: Read>( let mut local_poisoned = false; + // Scan through the test file to handle `ignore-*`, `only-*`, and `needs-*` directives. iter_header( config.mode, &config.suite, &mut local_poisoned, path, src, - &mut |HeaderLine { header_revision, original_line, directive: ln, line_number }| { + &mut |DirectiveLine { header_revision, directive: ln, line_number }| { if header_revision.is_some() && header_revision != test_revision { return; } @@ -1317,17 +1317,7 @@ pub fn make_test_description<R: Read>( }; } - if let Some((_, post)) = original_line.trim_start().split_once("//") { - let post = post.trim_start(); - if post.starts_with("ignore-tidy") { - // Not handled by compiletest. - } else { - decision!(cfg::handle_ignore(config, ln)); - } - } else { - decision!(cfg::handle_ignore(config, ln)); - } - + decision!(cfg::handle_ignore(config, ln)); decision!(cfg::handle_only(config, ln)); decision!(needs::handle_needs(&cache.needs, config, ln)); decision!(ignore_llvm(config, ln)); diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 22dfa349e2b..c3c9496c4d2 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -423,7 +423,7 @@ fn test_extract_version_range() { } #[test] -#[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")] +#[should_panic(expected = "duplicate revision: `rpass1` in line ` rpass1 rpass1`")] fn test_duplicate_revisions() { let config: Config = cfg().build(); parse_rs(&config, "//@ revisions: rpass1 rpass1"); diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 30d1644b148..2e66c084dd7 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -10,6 +10,7 @@ mod tests; pub mod common; pub mod compute_diff; +mod debuggers; pub mod errors; pub mod header; mod json; @@ -36,12 +37,17 @@ use walkdir::WalkDir; use self::header::{EarlyProps, make_test_description}; use crate::common::{ - Config, Debugger, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path, - output_base_dir, output_relative_path, + Config, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path, output_base_dir, + output_relative_path, }; use crate::header::HeadersCache; use crate::util::logv; +/// Creates the `Config` instance for this invocation of compiletest. +/// +/// The config mostly reflects command-line arguments, but there might also be +/// some code here that inspects environment variables or even runs executables +/// (e.g. when discovering debugger versions). pub fn parse_config(args: Vec<String>) -> Config { let mut opts = Options::new(); opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH") @@ -204,9 +210,11 @@ pub fn parse_config(args: Vec<String>) -> Config { let target = opt_str2(matches.opt_str("target")); let android_cross_path = opt_path(matches, "android-cross-path"); - let (cdb, cdb_version) = analyze_cdb(matches.opt_str("cdb"), &target); - let (gdb, gdb_version) = analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path); - let lldb_version = matches.opt_str("lldb-version").as_deref().and_then(extract_lldb_version); + let (cdb, cdb_version) = debuggers::analyze_cdb(matches.opt_str("cdb"), &target); + let (gdb, gdb_version) = + debuggers::analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path); + let lldb_version = + matches.opt_str("lldb-version").as_deref().and_then(debuggers::extract_lldb_version); let color = match matches.opt_str("color").as_deref() { Some("auto") | None => ColorConfig::AutoColor, Some("always") => ColorConfig::AlwaysColor, @@ -410,6 +418,7 @@ pub fn opt_str2(maybestr: Option<String>) -> String { } } +/// Called by `main` after the config has been parsed. pub fn run_tests(config: Arc<Config>) { // If we want to collect rustfix coverage information, // we first make sure that the coverage file does not exist. @@ -443,14 +452,16 @@ pub fn run_tests(config: Arc<Config>) { if let Mode::DebugInfo = config.mode { // Debugging emscripten code doesn't make sense today if !config.target.contains("emscripten") { - configs.extend(configure_cdb(&config)); - configs.extend(configure_gdb(&config)); - configs.extend(configure_lldb(&config)); + configs.extend(debuggers::configure_cdb(&config)); + configs.extend(debuggers::configure_gdb(&config)); + configs.extend(debuggers::configure_lldb(&config)); } } else { configs.push(config.clone()); }; + // Discover all of the tests in the test suite directory, and build a libtest + // structure for each test (or each revision of a multi-revision test). let mut tests = Vec::new(); for c in configs { let mut found_paths = HashSet::new(); @@ -460,7 +471,12 @@ pub fn run_tests(config: Arc<Config>) { tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice())); + // Delegate to libtest to filter and run the big list of structures created + // during test discovery. When libtest decides to run a test, it will invoke + // the corresponding closure created by `make_test_closure`. let res = test::run_tests_console(&opts, tests); + + // Check the outcome reported by libtest. match res { Ok(true) => {} Ok(false) => { @@ -498,62 +514,6 @@ pub fn run_tests(config: Arc<Config>) { } } -fn configure_cdb(config: &Config) -> Option<Arc<Config>> { - config.cdb.as_ref()?; - - Some(Arc::new(Config { debugger: Some(Debugger::Cdb), ..config.clone() })) -} - -fn configure_gdb(config: &Config) -> Option<Arc<Config>> { - config.gdb_version?; - - if config.matches_env("msvc") { - return None; - } - - if config.remote_test_client.is_some() && !config.target.contains("android") { - println!( - "WARNING: debuginfo tests are not available when \ - testing with remote" - ); - return None; - } - - if config.target.contains("android") { - println!( - "{} debug-info test uses tcp 5039 port.\ - please reserve it", - config.target - ); - - // android debug-info test uses remote debugger so, we test 1 thread - // at once as they're all sharing the same TCP port to communicate - // over. - // - // we should figure out how to lift this restriction! (run them all - // on different ports allocated dynamically). - env::set_var("RUST_TEST_THREADS", "1"); - } - - Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() })) -} - -fn configure_lldb(config: &Config) -> Option<Arc<Config>> { - config.lldb_python_dir.as_ref()?; - - if let Some(350) = config.lldb_version { - println!( - "WARNING: The used version of LLDB (350) has a \ - known issue that breaks debuginfo tests. See \ - issue #32520 for more information. Skipping all \ - LLDB-based tests!", - ); - return None; - } - - Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() })) -} - pub fn test_opts(config: &Config) -> test::TestOpts { if env::var("RUST_TEST_NOCAPTURE").is_ok() { eprintln!( @@ -585,6 +545,11 @@ pub fn test_opts(config: &Config) -> test::TestOpts { } } +/// Creates libtest structures for every test/revision in the test suite directory. +/// +/// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests), +/// regardless of whether any filters/tests were specified on the command-line, +/// because filtering is handled later by libtest. pub fn make_tests( config: Arc<Config>, tests: &mut Vec<test::TestDescAndFn>, @@ -663,10 +628,17 @@ fn common_inputs_stamp(config: &Config) -> Stamp { stamp } +/// Returns a list of modified/untracked test files that should be run when +/// the `--only-modified` flag is in use. +/// +/// (Might be inaccurate in some cases.) fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> { + // If `--only-modified` wasn't passed, the list of modified tests won't be + // used for anything, so avoid some work and just return an empty list. if !config.only_modified { return Ok(vec![]); } + let files = get_git_modified_files(&config.git_config(), Some(dir), &vec!["rs", "stderr", "fixed"])? .unwrap_or(vec![]); @@ -687,6 +659,8 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> { Ok(full_paths) } +/// Recursively scans a directory to find test files and create test structures +/// that will be handed over to libtest. fn collect_tests_from_dir( config: Arc<Config>, cache: &HeadersCache, @@ -703,6 +677,8 @@ fn collect_tests_from_dir( return Ok(()); } + // For run-make tests, a "test file" is actually a directory that contains + // an `rmake.rs` or `Makefile`" if config.mode == Mode::RunMake { if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() { return Err(io::Error::other( @@ -716,6 +692,7 @@ fn collect_tests_from_dir( relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), }; tests.extend(make_test(config, cache, &paths, inputs, poisoned)); + // This directory is a test, so don't try to find other tests inside it. return Ok(()); } } @@ -730,22 +707,27 @@ fn collect_tests_from_dir( fs::create_dir_all(&build_dir).unwrap(); // Add each `.rs` file as a test, and recurse further on any - // subdirectories we find, except for `aux` directories. + // subdirectories we find, except for `auxiliary` directories. // FIXME: this walks full tests tree, even if we have something to ignore // use walkdir/ignore like in tidy? for file in fs::read_dir(dir)? { let file = file?; let file_path = file.path(); let file_name = file.file_name(); + if is_test(&file_name) && (!config.only_modified || modified_tests.contains(&file_path)) { + // We found a test file, so create the corresponding libtest structures. debug!("found test file: {:?}", file_path.display()); + + // Record the stem of the test file, to check for overlaps later. let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); found_paths.insert(rel_test_path); + let paths = TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; - tests.extend(make_test(config.clone(), cache, &paths, inputs, poisoned)) } else if file_path.is_dir() { + // Recurse to find more tests in a subdirectory. let relative_file_path = relative_dir_path.join(file.file_name()); if &file_name != "auxiliary" { debug!("found directory: {:?}", file_path.display()); @@ -781,6 +763,8 @@ pub fn is_test(file_name: &OsString) -> bool { !invalid_prefixes.iter().any(|p| file_name.starts_with(p)) } +/// For a single test file, creates one or more test structures (one per revision) +/// that can be handed over to libtest to run, possibly in parallel. fn make_test( config: Arc<Config>, cache: &HeadersCache, @@ -788,6 +772,9 @@ fn make_test( inputs: &Stamp, poisoned: &mut bool, ) -> Vec<test::TestDescAndFn> { + // For run-make tests, each "test file" is actually a _directory_ containing + // an `rmake.rs` or `Makefile`. But for the purposes of directive parsing, + // we want to look at that recipe file, not the directory itself. let test_path = if config.mode == Mode::RunMake { if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() { panic!("run-make tests cannot have both `rmake.rs` and `Makefile`"); @@ -803,26 +790,40 @@ fn make_test( } else { PathBuf::from(&testpaths.file) }; + + // Scan the test file to discover its revisions, if any. let early_props = EarlyProps::from_file(&config, &test_path); - // Incremental tests are special, they inherently cannot be run in parallel. - // `runtest::run` will be responsible for iterating over revisions. + // Normally we create one libtest structure per revision, with two exceptions: + // - If a test doesn't use revisions, create a dummy revision (None) so that + // the test can still run. + // - Incremental tests inherently can't run their revisions in parallel, so + // we treat them like non-revisioned tests here. Incremental revisions are + // handled internally by `runtest::run` instead. let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental { vec![None] } else { early_props.revisions.iter().map(|r| Some(r.as_str())).collect() }; + // For each revision (or the sole dummy revision), create and return a + // `test::TestDescAndFn` that can be handed over to libtest. revisions .into_iter() .map(|revision| { + // Create a test name and description to hand over to libtest. let src_file = std::fs::File::open(&test_path).expect("open test file to parse ignores"); let test_name = crate::make_test_name(&config, testpaths, revision); + // Create a libtest description for the test/revision. + // This is where `ignore-*`/`only-*`/`needs-*` directives are handled, + // because they need to set the libtest ignored flag. let mut desc = make_test_description( &config, cache, test_name, &test_path, src_file, revision, poisoned, ); - // Ignore tests that already run and are up to date with respect to inputs. + + // If a test's inputs haven't changed since the last time it ran, + // mark it as ignored so that libtest will skip it. if !config.force_rerun && is_up_to_date(&config, testpaths, &early_props, revision, inputs) { @@ -830,18 +831,25 @@ fn make_test( // Keep this in sync with the "up-to-date" message detected by bootstrap. desc.ignore_message = Some("up-to-date"); } - test::TestDescAndFn { - desc, - testfn: make_test_closure(config.clone(), testpaths, revision), - } + + // Create the callback that will run this test/revision when libtest calls it. + let testfn = make_test_closure(config.clone(), testpaths, revision); + + test::TestDescAndFn { desc, testfn } }) .collect() } +/// The path of the `stamp` file that gets created or updated whenever a +/// particular test completes successfully. fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { output_base_dir(config, testpaths, revision).join("stamp") } +/// Returns a list of files that, if modified, would cause this test to no +/// longer be up-to-date. +/// +/// (Might be inaccurate in some cases.) fn files_related_to_test( config: &Config, testpaths: &TestPaths, @@ -877,46 +885,61 @@ fn files_related_to_test( related } +/// Checks whether a particular test/revision is "up-to-date", meaning that no +/// relevant files/settings have changed since the last time the test succeeded. +/// +/// (This is not very reliable in some circumstances, so the `--force-rerun` +/// flag can be used to ignore up-to-date checking and always re-run tests.) fn is_up_to_date( config: &Config, testpaths: &TestPaths, props: &EarlyProps, revision: Option<&str>, - inputs: &Stamp, + inputs: &Stamp, // Last-modified timestamp of the compiler, compiletest etc ) -> bool { let stamp_name = stamp(config, testpaths, revision); - // Check hash. + // Check the config hash inside the stamp file. let contents = match fs::read_to_string(&stamp_name) { Ok(f) => f, Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"), + // The test hasn't succeeded yet, so it is not up-to-date. Err(_) => return false, }; let expected_hash = runtest::compute_stamp_hash(config); if contents != expected_hash { + // Some part of compiletest configuration has changed since the test + // last succeeded, so it is not up-to-date. return false; } - // Check timestamps. + // Check the timestamp of the stamp file against the last modified time + // of all files known to be relevant to the test. let mut inputs = inputs.clone(); for path in files_related_to_test(config, testpaths, props, revision) { inputs.add_path(&path); } + // If no relevant files have been modified since the stamp file was last + // written, the test is up-to-date. inputs < Stamp::from_path(&stamp_name) } +/// The maximum of a set of file-modified timestamps. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] struct Stamp { time: SystemTime, } impl Stamp { + /// Creates a timestamp holding the last-modified time of the specified file. fn from_path(path: &Path) -> Self { let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH }; stamp.add_path(path); stamp } + /// Updates this timestamp to the last-modified time of the specified file, + /// if it is later than the currently-stored timestamp. fn add_path(&mut self, path: &Path) { let modified = fs::metadata(path) .and_then(|metadata| metadata.modified()) @@ -924,6 +947,9 @@ impl Stamp { self.time = self.time.max(modified); } + /// Updates this timestamp to the most recent last-modified time of all files + /// recursively contained in the given directory, if it is later than the + /// currently-stored timestamp. fn add_dir(&mut self, path: &Path) { for entry in WalkDir::new(path) { let entry = entry.unwrap(); @@ -939,6 +965,7 @@ impl Stamp { } } +/// Creates a name for this test/revision that can be handed over to libtest. fn make_test_name( config: &Config, testpaths: &TestPaths, @@ -967,226 +994,41 @@ fn make_test_name( )) } +/// Creates a callback for this test/revision that libtest will call when it +/// decides to actually run the underlying test. fn make_test_closure( config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>, ) -> test::TestFn { - let config = config.clone(); let testpaths = testpaths.clone(); let revision = revision.map(str::to_owned); + + // This callback is the link between compiletest's test discovery code, + // and the parts of compiletest that know how to run an individual test. test::DynTestFn(Box::new(move || { runtest::run(config, &testpaths, revision.as_deref()); Ok(()) })) } -/// Returns `true` if the given target is an Android target for the -/// purposes of GDB testing. -fn is_android_gdb_target(target: &str) -> bool { - matches!( - &target[..], - "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" - ) -} - -/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing. -fn is_pc_windows_msvc_target(target: &str) -> bool { - target.ends_with("-pc-windows-msvc") -} - -fn find_cdb(target: &str) -> Option<OsString> { - if !(cfg!(windows) && is_pc_windows_msvc_target(target)) { - return None; - } - - let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?; - let cdb_arch = if cfg!(target_arch = "x86") { - "x86" - } else if cfg!(target_arch = "x86_64") { - "x64" - } else if cfg!(target_arch = "aarch64") { - "arm64" - } else if cfg!(target_arch = "arm") { - "arm" - } else { - return None; // No compatible CDB.exe in the Windows 10 SDK - }; - - let mut path = PathBuf::new(); - path.push(pf86); - path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too? - path.push(cdb_arch); - path.push(r"cdb.exe"); - - if !path.exists() { - return None; - } - - Some(path.into_os_string()) -} - -/// Returns Path to CDB -fn analyze_cdb(cdb: Option<String>, target: &str) -> (Option<OsString>, Option<[u16; 4]>) { - let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target)); - - let mut version = None; - if let Some(cdb) = cdb.as_ref() { - if let Ok(output) = Command::new(cdb).arg("/version").output() { - if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() { - version = extract_cdb_version(&first_line); - } - } - } - - (cdb, version) -} - -fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> { - // Example full_version_line: "cdb version 10.0.18362.1" - let version = full_version_line.rsplit(' ').next()?; - let mut components = version.split('.'); - let major: u16 = components.next().unwrap().parse().unwrap(); - let minor: u16 = components.next().unwrap().parse().unwrap(); - let patch: u16 = components.next().unwrap_or("0").parse().unwrap(); - let build: u16 = components.next().unwrap_or("0").parse().unwrap(); - Some([major, minor, patch, build]) -} - -/// Returns (Path to GDB, GDB Version) -fn analyze_gdb( - gdb: Option<String>, - target: &str, - android_cross_path: &PathBuf, -) -> (Option<String>, Option<u32>) { - #[cfg(not(windows))] - const GDB_FALLBACK: &str = "gdb"; - #[cfg(windows)] - const GDB_FALLBACK: &str = "gdb.exe"; - - let fallback_gdb = || { - if is_android_gdb_target(target) { - let mut gdb_path = match android_cross_path.to_str() { - Some(x) => x.to_owned(), - None => panic!("cannot find android cross path"), - }; - gdb_path.push_str("/bin/gdb"); - gdb_path - } else { - GDB_FALLBACK.to_owned() - } - }; - - let gdb = match gdb { - None => fallback_gdb(), - Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb - Some(ref s) => s.to_owned(), - }; - - let mut version_line = None; - if let Ok(output) = Command::new(&gdb).arg("--version").output() { - if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() { - version_line = Some(first_line.to_string()); - } - } - - let version = match version_line { - Some(line) => extract_gdb_version(&line), - None => return (None, None), - }; - - (Some(gdb), version) -} - -fn extract_gdb_version(full_version_line: &str) -> Option<u32> { - let full_version_line = full_version_line.trim(); - - // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both - // of the ? sections being optional - - // We will parse up to 3 digits for each component, ignoring the date - - // We skip text in parentheses. This avoids accidentally parsing - // the openSUSE version, which looks like: - // GNU gdb (GDB; openSUSE Leap 15.0) 8.1 - // This particular form is documented in the GNU coding standards: - // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion - - let unbracketed_part = full_version_line.split('[').next().unwrap(); - let mut splits = unbracketed_part.trim_end().rsplit(' '); - let version_string = splits.next().unwrap(); - - let mut splits = version_string.split('.'); - let major = splits.next().unwrap(); - let minor = splits.next().unwrap(); - let patch = splits.next(); - - let major: u32 = major.parse().unwrap(); - let (minor, patch): (u32, u32) = match minor.find(not_a_digit) { - None => { - let minor = minor.parse().unwrap(); - let patch: u32 = match patch { - Some(patch) => match patch.find(not_a_digit) { - None => patch.parse().unwrap(), - Some(idx) if idx > 3 => 0, - Some(idx) => patch[..idx].parse().unwrap(), - }, - None => 0, - }; - (minor, patch) - } - // There is no patch version after minor-date (e.g. "4-2012"). - Some(idx) => { - let minor = minor[..idx].parse().unwrap(); - (minor, 0) - } - }; - - Some(((major * 1000) + minor) * 1000 + patch) -} - -/// Returns LLDB version -fn extract_lldb_version(full_version_line: &str) -> Option<u32> { - // Extract the major LLDB version from the given version string. - // LLDB version strings are different for Apple and non-Apple platforms. - // The Apple variant looks like this: - // - // LLDB-179.5 (older versions) - // lldb-300.2.51 (new versions) - // - // We are only interested in the major version number, so this function - // will return `Some(179)` and `Some(300)` respectively. - // - // Upstream versions look like: - // lldb version 6.0.1 - // - // There doesn't seem to be a way to correlate the Apple version - // with the upstream version, and since the tests were originally - // written against Apple versions, we make a fake Apple version by - // multiplying the first number by 100. This is a hack. - - let full_version_line = full_version_line.trim(); - - if let Some(apple_ver) = - full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-")) - { - if let Some(idx) = apple_ver.find(not_a_digit) { - let version: u32 = apple_ver[..idx].parse().unwrap(); - return Some(version); - } - } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") { - if let Some(idx) = lldb_ver.find(not_a_digit) { - let version: u32 = lldb_ver[..idx].parse().ok()?; - return Some(version * 100); - } - } - None -} - -fn not_a_digit(c: char) -> bool { - !c.is_ascii_digit() -} - +/// Checks that test discovery didn't find any tests whose name stem is a prefix +/// of some other tests's name. +/// +/// For example, suppose the test suite contains these two test files: +/// - `tests/rustdoc/primitive.rs` +/// - `tests/rustdoc/primitive/no_std.rs` +/// +/// The test runner might put the output from those tests in these directories: +/// - `$build/test/rustdoc/primitive/` +/// - `$build/test/rustdoc/primitive/no_std/` +/// +/// Because one output path is a subdirectory of the other, the two tests might +/// interfere with each other in unwanted ways, especially if the test runner +/// decides to delete test output directories to clean them between runs. +/// To avoid problems, we forbid test names from overlapping in this way. +/// +/// See <https://github.com/rust-lang/rust/pull/109509> for more context. fn check_overlapping_tests(found_paths: &HashSet<PathBuf>) { let mut collisions = Vec::new(); for path in found_paths { diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 46f7b9c0e7d..29f9925de16 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -318,10 +318,29 @@ impl<'test> TestCx<'test> { } } - fn check_if_test_should_compile(&self, proc_res: &ProcRes, pm: Option<PassMode>) { - if self.should_compile_successfully(pm) { + fn check_if_test_should_compile( + &self, + fail_mode: Option<FailMode>, + pass_mode: Option<PassMode>, + proc_res: &ProcRes, + ) { + if self.should_compile_successfully(pass_mode) { if !proc_res.status.success() { - self.fatal_proc_rec("test compilation failed although it shouldn't!", proc_res); + match (fail_mode, pass_mode) { + (Some(FailMode::Build), Some(PassMode::Check)) => { + // A `build-fail` test needs to `check-pass`. + self.fatal_proc_rec( + "`build-fail` test is required to pass check build, but check build failed", + proc_res, + ); + } + _ => { + self.fatal_proc_rec( + "test compilation failed although it shouldn't!", + proc_res, + ); + } + } } } else { if proc_res.status.success() { diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs index 36127414ab1..bd0845b4524 100644 --- a/src/tools/compiletest/src/runtest/debuginfo.rs +++ b/src/tools/compiletest/src/runtest/debuginfo.rs @@ -9,8 +9,8 @@ use tracing::debug; use super::debugger::DebuggerCommands; use super::{Debugger, Emit, ProcRes, TestCx, Truncated, WillExecute}; use crate::common::Config; +use crate::debuggers::{extract_gdb_version, is_android_gdb_target}; use crate::util::logv; -use crate::{extract_gdb_version, is_android_gdb_target}; impl TestCx<'_> { pub(super) fn run_debuginfo_test(&self) { diff --git a/src/tools/compiletest/src/runtest/incremental.rs b/src/tools/compiletest/src/runtest/incremental.rs index bf2b71fef43..591aff0defe 100644 --- a/src/tools/compiletest/src/runtest/incremental.rs +++ b/src/tools/compiletest/src/runtest/incremental.rs @@ -1,4 +1,4 @@ -use super::{TestCx, WillExecute}; +use super::{FailMode, TestCx, WillExecute}; use crate::errors; impl TestCx<'_> { @@ -96,7 +96,7 @@ impl TestCx<'_> { fn run_cfail_test(&self) { let pm = self.pass_mode(); let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm)); - self.check_if_test_should_compile(&proc_res, pm); + self.check_if_test_should_compile(Some(FailMode::Build), pm, &proc_res); self.check_no_compiler_crash(&proc_res, self.props.should_ice); let output_to_check = self.get_output(&proc_res); diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index bd8ef952a86..bb747c68029 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -18,14 +18,14 @@ impl TestCx<'_> { let pm = Some(PassMode::Check); let proc_res = self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new()); - self.check_if_test_should_compile(&proc_res, pm); + self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res); } let pm = self.pass_mode(); let should_run = self.should_run(pm); let emit_metadata = self.should_emit_metadata(pm); let proc_res = self.compile_test(should_run, emit_metadata); - self.check_if_test_should_compile(&proc_res, pm); + self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res); if matches!(proc_res.truncated, Truncated::Yes) && !self.props.dont_check_compiler_stdout && !self.props.dont_check_compiler_stderr diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index 7c2e7b0f023..680579c59ae 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -1,5 +1,8 @@ -use super::header::extract_llvm_version; -use super::*; +use std::ffi::OsString; + +use crate::debuggers::{extract_gdb_version, extract_lldb_version}; +use crate::header::extract_llvm_version; +use crate::is_test; #[test] fn test_extract_gdb_version() { diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index ca03a9b16e3..d0bcf68eacb 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -45,6 +45,14 @@ process for such contributions: This process is largely informal, and its primary goal is to more clearly communicate expectations. Please get in touch with us if you have any questions! +### Managing the review state + +Most PRs bounce back and forth between the reviewer and the author several times, so it is good to +keep track of who is expected to take the next step. We are using the `S-waiting-for-review` and +`S-waiting-for-author` labels for that. If a reviewer asked you to do some changes and you think +they are all taken care of, post a comment saying `@rustbot ready` to mark a PR as ready for the +next round of review. + ## Preparing the build environment Miri heavily relies on internal and unstable rustc interfaces to execute MIR, @@ -195,48 +203,37 @@ installed (`cargo install hyperfine`). ## Configuring `rust-analyzer` -To configure `rust-analyzer` and VS Code for working on Miri, save the following -to `.vscode/settings.json` in your local Miri clone: - -```json -{ - "rust-analyzer.rustc.source": "discover", - "rust-analyzer.linkedProjects": [ - "Cargo.toml", - "cargo-miri/Cargo.toml", - "miri-script/Cargo.toml", - ], - "rust-analyzer.check.invocationLocation": "root", - "rust-analyzer.check.invocationStrategy": "once", - "rust-analyzer.check.overrideCommand": [ - "env", - "MIRI_AUTO_OPS=no", - "./miri", - "clippy", // make this `check` when working with a locally built rustc - "--message-format=json", - ], - // Contrary to what the name suggests, this also affects proc macros. - "rust-analyzer.cargo.buildScripts.invocationLocation": "root", - "rust-analyzer.cargo.buildScripts.invocationStrategy": "once", - "rust-analyzer.cargo.buildScripts.overrideCommand": [ - "env", - "MIRI_AUTO_OPS=no", - "./miri", - "check", - "--message-format=json", - ], -} -``` +To configure `rust-analyzer` and the IDE for working on Miri, copy one of the provided +configuration files according to the instructions below. You can also set up a symbolic +link to keep the configuration in sync with our recommendations. + +### Visual Studio Code + +Copy [`etc/rust_analyzer_vscode.json`] to `.vscode/settings.json` in the project root directory. + +[`etc/rust_analyzer_vscode.json`]: https://github.com/rust-lang/miri/blob/master/etc/rust_analyzer_vscode.json + +### Helix + +Copy [`etc/rust_analyzer_helix.toml`] to `.helix/languages.toml` in the project root directory. + +Since working on Miri requires a custom toolchain, and Helix requires the language server +to be installed with the toolchain, you have to run `./miri toolchain -c rust-analyzer` +when installing the Miri toolchain. Alternatively, set the `RUSTUP_TOOLCHAIN` environment variable according to +[the documentation](https://rust-analyzer.github.io/manual.html#toolchain). + +[`etc/rust_analyzer_helix.toml`]: https://github.com/rust-lang/miri/blob/master/etc/rust_analyzer_helix.toml + +### Advanced configuration -> #### Note -> -> If you are [building Miri with a locally built rustc][], set -> `rust-analyzer.rustcSource` to the relative path from your Miri clone to the -> root `Cargo.toml` of the locally built rustc. For example, the path might look -> like `../rust/Cargo.toml`. +If you are building Miri with a locally built rustc, set +`rust-analyzer.rustcSource` to the relative path from your Miri clone to the +root `Cargo.toml` of the locally built rustc. For example, the path might look +like `../rust/Cargo.toml`. In addition to that, replace `clippy` by `check` +in the `rust-analyzer.check.overrideCommand` setting. See the rustc-dev-guide's docs on ["Configuring `rust-analyzer` for `rustc`"][rdg-r-a] -for more information about configuring VS Code and `rust-analyzer`. +for more information about configuring the IDE and `rust-analyzer`. [rdg-r-a]: https://rustc-dev-guide.rust-lang.org/building/suggested.html#configuring-rust-analyzer-for-rustc diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index f6349f45f43..a73fefaaf34 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -290,7 +290,7 @@ environment variable. We first document the most relevant and most commonly used * `-Zmiri-compare-exchange-weak-failure-rate=<rate>` changes the failure rate of `compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail). You can change it to any value between `0.0` and `1.0`, where `1.0` means it - will always fail and `0.0` means it will never fail. Note than setting it to + will always fail and `0.0` means it will never fail. Note that setting it to `1.0` will likely cause hangs, since it means programs using `compare_exchange_weak` cannot make progress. * `-Zmiri-disable-isolation` disables host isolation. As a consequence, @@ -392,11 +392,6 @@ to Miri failing to detect cases of undefined behavior in a program. but reports to the program that it did actually write. This is useful when you are not interested in the actual program's output, but only want to see Miri's errors and warnings. -* `-Zmiri-panic-on-unsupported` will makes some forms of unsupported functionality, - such as FFI and unsupported syscalls, panic within the context of the emulated - application instead of raising an error within the context of Miri (and halting - execution). Note that code might not expect these operations to ever panic, so - this flag can lead to strange (mis)behavior. * `-Zmiri-recursive-validation` is a *highly experimental* flag that makes validity checking recurse below references. * `-Zmiri-retag-fields[=<all|none|scalar>]` controls when Stacked Borrows retagging recurses into diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index a873472fd5d..b8e08d39a86 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "anyhow" @@ -202,9 +202,9 @@ dependencies = [ [[package]] name = "rustc_tools_util" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f" +checksum = "3316159ab19e19d1065ecc49278e87f767a9dae9fae80348d2b4d4fa4ae02d4d" [[package]] name = "rustc_version" diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index ee2004278b4..de0988d6d1c 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -26,4 +26,4 @@ rustc-build-sysroot = "0.5.4" serde = { version = "1.0.185", features = ["derive"] } [build-dependencies] -rustc_tools_util = "0.3" +rustc_tools_util = "0.4" diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 689bc6d46fc..ad1b2f4d0c3 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -150,10 +150,10 @@ case $HOST_TARGET in # Partially supported targets (tier 2) BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there - TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs - TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs - TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls - TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls + TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe + TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe + TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe + TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap pthread --skip threadname TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std diff --git a/src/tools/miri/etc/rust_analyzer_helix.toml b/src/tools/miri/etc/rust_analyzer_helix.toml new file mode 100644 index 00000000000..62db463a191 --- /dev/null +++ b/src/tools/miri/etc/rust_analyzer_helix.toml @@ -0,0 +1,32 @@ +[language-server.rust-analyzer.config.rustc] +source = "discover" + +[language-server.rust-analyzer.config] +linkedProjects = [ + "Cargo.toml", + "cargo-miri/Cargo.toml", + "miri-script/Cargo.toml", +] + +[language-server.rust-analyzer.config.check] +invocationLocation = "root" +invocationStrategy = "once" +overrideCommand = [ + "env", + "MIRI_AUTO_OPS=no", + "./miri", + "clippy", # make this `check` when working with a locally built rustc + "--message-format=json", +] + +# Contrary to what the name suggests, this also affects proc macros. +[language-server.rust-analyzer.config.buildScripts] +invocationLocation = "root" +invocationStrategy = "once" +overrideCommand = [ + "env", + "MIRI_AUTO_OPS=no", + "./miri", + "check", + "--message-format=json", +] diff --git a/src/tools/miri/etc/rust_analyzer_vscode.json b/src/tools/miri/etc/rust_analyzer_vscode.json new file mode 100644 index 00000000000..5e51c3e8880 --- /dev/null +++ b/src/tools/miri/etc/rust_analyzer_vscode.json @@ -0,0 +1,27 @@ +{ + "rust-analyzer.rustc.source": "discover", + "rust-analyzer.linkedProjects": [ + "Cargo.toml", + "cargo-miri/Cargo.toml", + "miri-script/Cargo.toml", + ], + "rust-analyzer.check.invocationLocation": "root", + "rust-analyzer.check.invocationStrategy": "once", + "rust-analyzer.check.overrideCommand": [ + "env", + "MIRI_AUTO_OPS=no", + "./miri", + "clippy", // make this `check` when working with a locally built rustc + "--message-format=json", + ], + // Contrary to what the name suggests, this also affects proc macros. + "rust-analyzer.cargo.buildScripts.invocationLocation": "root", + "rust-analyzer.cargo.buildScripts.invocationStrategy": "once", + "rust-analyzer.cargo.buildScripts.overrideCommand": [ + "env", + "MIRI_AUTO_OPS=no", + "./miri", + "check", + "--message-format=json", + ], +} diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index eb4dfcf57cf..8b9e7efdff9 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -7067e4aee45c18cfa1c6af3bf79bd097684fb294 +17a19e684cdf3ca088af8b4da6a6209d128913f4 diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index a13b14ca90a..50e55268248 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -453,7 +453,7 @@ impl<'tcx> MiriMachine<'tcx> { let thread = self.threads.active_thread(); global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { if let Some(data_race) = &self.data_race { - data_race.release_clock(&self.threads).clone() + data_race.release_clock(&self.threads, |clock| clock.clone()) } else { VClock::default() } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 8d3ae97e0e9..717229ba8b3 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -530,8 +530,6 @@ fn main() { } else if arg == "-Zmiri-ignore-leaks" { miri_config.ignore_leaks = true; miri_config.collect_leak_backtraces = false; - } else if arg == "-Zmiri-panic-on-unsupported" { - miri_config.panic_on_unsupported = true; } else if arg == "-Zmiri-strict-provenance" { miri_config.provenance_mode = ProvenanceMode::Strict; } else if arg == "-Zmiri-permissive-provenance" { diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 82c4f6d3007..797b3191d83 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -828,15 +828,14 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { } } - /// Returns the `release` clock of the current thread. + /// Calls the callback with the "release" clock of the current thread. /// Other threads can acquire this clock in the future to establish synchronization /// with this program point. - fn release_clock<'a>(&'a self) -> Option<Ref<'a, VClock>> - where - 'tcx: 'a, - { + /// + /// The closure will only be invoked if data race handling is on. + fn release_clock<R>(&self, callback: impl FnOnce(&VClock) -> R) -> Option<R> { let this = self.eval_context_ref(); - Some(this.machine.data_race.as_ref()?.release_clock(&this.machine.threads)) + Some(this.machine.data_race.as_ref()?.release_clock(&this.machine.threads, callback)) } /// Acquire the given clock into the current thread, establishing synchronization with @@ -1728,7 +1727,7 @@ impl GlobalState { let current_index = self.active_thread_index(thread_mgr); // Store the terminaion clock. - let terminaion_clock = self.release_clock(thread_mgr).clone(); + let terminaion_clock = self.release_clock(thread_mgr, |clock| clock.clone()); self.thread_info.get_mut()[current_thread].termination_vector_clock = Some(terminaion_clock); @@ -1778,21 +1777,23 @@ impl GlobalState { clocks.clock.join(clock); } - /// Returns the `release` clock of the current thread. + /// Calls the given closure with the "release" clock of the current thread. /// Other threads can acquire this clock in the future to establish synchronization /// with this program point. - pub fn release_clock<'tcx>(&self, threads: &ThreadManager<'tcx>) -> Ref<'_, VClock> { + pub fn release_clock<'tcx, R>( + &self, + threads: &ThreadManager<'tcx>, + callback: impl FnOnce(&VClock) -> R, + ) -> R { let thread = threads.active_thread(); let span = threads.active_thread_ref().current_span(); - // We increment the clock each time this happens, to ensure no two releases - // can be confused with each other. let (index, mut clocks) = self.thread_state_mut(thread); + let r = callback(&clocks.clock); + // Increment the clock, so that all following events cannot be confused with anything that + // occurred before the release. Crucially, the callback is invoked on the *old* clock! clocks.increment_clock(index, span); - drop(clocks); - // To return a read-only view, we need to release the RefCell - // and borrow it again. - let (_index, clocks) = self.thread_state(thread); - Ref::map(clocks, |c| &c.clock) + + r } fn thread_index(&self, thread: ThreadId) -> VectorIdx { diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 7a9b12bbe82..534f02545bd 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -2,7 +2,7 @@ use std::collections::VecDeque; use rustc_index::Idx; -use super::sync::EvalContextExtPriv as _; +use super::thread::DynUnblockCallback; use super::vector_clock::VClock; use crate::*; @@ -27,22 +27,6 @@ pub(super) struct InitOnce { impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn init_once_get_or_create_id( - &mut self, - lock: &MPlaceTy<'tcx>, - offset: u64, - ) -> InterpResult<'tcx, InitOnceId> { - let this = self.eval_context_mut(); - this.get_or_create_id( - lock, - offset, - |ecx| &mut ecx.machine.sync.init_onces, - |_| interp_ok(Default::default()), - )? - .ok_or_else(|| err_ub_format!("init_once has invalid ID")) - .into() - } - #[inline] fn init_once_status(&mut self, id: InitOnceId) -> InitOnceStatus { let this = self.eval_context_ref(); @@ -51,11 +35,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Put the thread into the queue waiting for the initialization. #[inline] - fn init_once_enqueue_and_block( - &mut self, - id: InitOnceId, - callback: impl UnblockCallback<'tcx> + 'tcx, - ) { + fn init_once_enqueue_and_block(&mut self, id: InitOnceId, callback: DynUnblockCallback<'tcx>) { let this = self.eval_context_mut(); let thread = this.active_thread(); let init_once = &mut this.machine.sync.init_onces[id]; @@ -93,7 +73,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Each complete happens-before the end of the wait if let Some(data_race) = &this.machine.data_race { - init_once.clock.clone_from(&data_race.release_clock(&this.machine.threads)); + data_race + .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock)); } // Wake up everyone. @@ -119,7 +100,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Each complete happens-before the end of the wait if let Some(data_race) = &this.machine.data_race { - init_once.clock.clone_from(&data_race.release_clock(&this.machine.threads)); + data_race + .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock)); } // Wake up one waiting thread, so they can go ahead and try to init this. diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 5627ccdbbea..199aedfa6d2 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -1,4 +1,3 @@ -use std::any::Any; use std::collections::VecDeque; use std::collections::hash_map::Entry; use std::ops::Not; @@ -12,11 +11,6 @@ use super::init_once::InitOnce; use super::vector_clock::VClock; use crate::*; -pub trait SyncId { - fn from_u32(id: u32) -> Self; - fn to_u32(&self) -> u32; -} - /// We cannot use the `newtype_index!` macro because we have to use 0 as a /// sentinel value meaning that the identifier is not assigned. This is because /// the pthreads static initializers initialize memory with zeros (see the @@ -28,16 +22,6 @@ macro_rules! declare_id { #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct $name(std::num::NonZero<u32>); - impl $crate::concurrency::sync::SyncId for $name { - // Panics if `id == 0`. - fn from_u32(id: u32) -> Self { - Self(std::num::NonZero::new(id).unwrap()) - } - fn to_u32(&self) -> u32 { - self.0.get() - } - } - impl $crate::VisitProvenance for $name { fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {} } @@ -56,12 +40,6 @@ macro_rules! declare_id { usize::try_from(self.0.get() - 1).unwrap() } } - - impl $name { - pub fn to_u32_scalar(&self) -> Scalar { - Scalar::from_u32(self.0.get()) - } - } }; } pub(super) use declare_id; @@ -79,9 +57,6 @@ struct Mutex { queue: VecDeque<ThreadId>, /// Mutex clock. This tracks the moment of the last unlock. clock: VClock, - - /// Additional data that can be set by shim implementations. - data: Option<Box<dyn Any>>, } declare_id!(RwLockId); @@ -118,9 +93,6 @@ struct RwLock { /// locks. /// This is only relevant when there is an active reader. clock_current_readers: VClock, - - /// Additional data that can be set by shim implementations. - data: Option<Box<dyn Any>>, } declare_id!(CondvarId); @@ -135,9 +107,6 @@ struct Condvar { /// Contains the clock of the last thread to /// perform a condvar-signal. clock: VClock, - - /// Additional data that can be set by shim implementations. - data: Option<Box<dyn Any>>, } /// The futex state. @@ -167,89 +136,15 @@ pub struct SynchronizationObjects { mutexes: IndexVec<MutexId, Mutex>, rwlocks: IndexVec<RwLockId, RwLock>, condvars: IndexVec<CondvarId, Condvar>, - futexes: FxHashMap<u64, Futex>, pub(super) init_onces: IndexVec<InitOnceId, InitOnce>, + + /// Futex info for the futex at the given address. + futexes: FxHashMap<u64, Futex>, } // Private extension trait for local helper methods impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { - /// Lazily initialize the ID of this Miri sync structure. - /// If memory stores '0', that indicates uninit and we generate a new instance. - /// Returns `None` if memory stores a non-zero invalid ID. - /// - /// `get_objs` must return the `IndexVec` that stores all the objects of this type. - /// `create_obj` must create the new object if initialization is needed. - #[inline] - fn get_or_create_id<Id: SyncId + Idx, T>( - &mut self, - lock: &MPlaceTy<'tcx>, - offset: u64, - get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>, - create_obj: impl for<'a> FnOnce(&'a mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>, - ) -> InterpResult<'tcx, Option<Id>> { - let this = self.eval_context_mut(); - let offset = Size::from_bytes(offset); - assert!(lock.layout.size >= offset + this.machine.layouts.u32.size); - let id_place = lock.offset(offset, this.machine.layouts.u32, this)?; - let next_index = get_objs(this).next_index(); - - // Since we are lazy, this update has to be atomic. - let (old, success) = this - .atomic_compare_exchange_scalar( - &id_place, - &ImmTy::from_uint(0u32, this.machine.layouts.u32), - Scalar::from_u32(next_index.to_u32()), - AtomicRwOrd::Relaxed, // deliberately *no* synchronization - AtomicReadOrd::Relaxed, - false, - )? - .to_scalar_pair(); - - interp_ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { - // We set the in-memory ID to `next_index`, now also create this object in the machine - // state. - let obj = create_obj(this)?; - let new_index = get_objs(this).push(obj); - assert_eq!(next_index, new_index); - Some(new_index) - } else { - let id = Id::from_u32(old.to_u32().expect("layout is u32")); - if get_objs(this).get(id).is_none() { - // The in-memory ID is invalid. - None - } else { - Some(id) - } - }) - } - - /// Eagerly creates a Miri sync structure. - /// - /// `create_id` will store the index of the sync_structure in the memory pointed to by - /// `lock_op`, so that future calls to `get_or_create_id` will see it as initialized. - /// - `lock_op` must hold a pointer to the sync structure. - /// - `lock_layout` must be the memory layout of the sync structure. - /// - `offset` must be the offset inside the sync structure where its miri id will be stored. - /// - `get_objs` is described in `get_or_create_id`. - /// - `obj` must be the new sync object. - fn create_id<Id: SyncId + Idx, T>( - &mut self, - lock: &MPlaceTy<'tcx>, - offset: u64, - get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>, - obj: T, - ) -> InterpResult<'tcx, Id> { - let this = self.eval_context_mut(); - let offset = Size::from_bytes(offset); - assert!(lock.layout.size >= offset + this.machine.layouts.u32.size); - let id_place = lock.offset(offset, this.machine.layouts.u32, this)?; - - let new_index = get_objs(this).push(obj); - this.write_scalar(Scalar::from_u32(new_index.to_u32()), &id_place)?; - interp_ok(new_index) - } - fn condvar_reacquire_mutex( &mut self, mutex: MutexId, @@ -270,124 +165,135 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } -// Public interface to synchronization primitives. Please note that in most -// cases, the function calls are infallible and it is the client's (shim -// implementation's) responsibility to detect and deal with erroneous -// situations. -impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} -pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - /// Eagerly create and initialize a new mutex. - fn mutex_create( - &mut self, - lock: &MPlaceTy<'tcx>, - offset: u64, - data: Option<Box<dyn Any>>, - ) -> InterpResult<'tcx, MutexId> { - let this = self.eval_context_mut(); - this.create_id(lock, offset, |ecx| &mut ecx.machine.sync.mutexes, Mutex { - data, - ..Default::default() - }) +impl SynchronizationObjects { + pub fn mutex_create(&mut self) -> MutexId { + self.mutexes.push(Default::default()) } - /// Lazily create a new mutex. - /// `initialize_data` must return any additional data that a user wants to associate with the mutex. - fn mutex_get_or_create_id( - &mut self, - lock: &MPlaceTy<'tcx>, - offset: u64, - initialize_data: impl for<'a> FnOnce( - &'a mut MiriInterpCx<'tcx>, - ) -> InterpResult<'tcx, Option<Box<dyn Any>>>, - ) -> InterpResult<'tcx, MutexId> { - let this = self.eval_context_mut(); - this.get_or_create_id( - lock, - offset, - |ecx| &mut ecx.machine.sync.mutexes, - |ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }), - )? - .ok_or_else(|| err_ub_format!("mutex has invalid ID")) - .into() + pub fn rwlock_create(&mut self) -> RwLockId { + self.rwlocks.push(Default::default()) } - /// Retrieve the additional data stored for a mutex. - fn mutex_get_data<'a, T: 'static>(&'a mut self, id: MutexId) -> Option<&'a T> - where - 'tcx: 'a, - { - let this = self.eval_context_ref(); - this.machine.sync.mutexes[id].data.as_deref().and_then(|p| p.downcast_ref::<T>()) + pub fn condvar_create(&mut self) -> CondvarId { + self.condvars.push(Default::default()) } - fn rwlock_get_or_create_id( - &mut self, - lock: &MPlaceTy<'tcx>, - offset: u64, - initialize_data: impl for<'a> FnOnce( - &'a mut MiriInterpCx<'tcx>, - ) -> InterpResult<'tcx, Option<Box<dyn Any>>>, - ) -> InterpResult<'tcx, RwLockId> { - let this = self.eval_context_mut(); - this.get_or_create_id( - lock, - offset, - |ecx| &mut ecx.machine.sync.rwlocks, - |ecx| initialize_data(ecx).map(|data| RwLock { data, ..Default::default() }), - )? - .ok_or_else(|| err_ub_format!("rwlock has invalid ID")) - .into() + pub fn init_once_create(&mut self) -> InitOnceId { + self.init_onces.push(Default::default()) } +} - /// Retrieve the additional data stored for a rwlock. - fn rwlock_get_data<'a, T: 'static>(&'a mut self, id: RwLockId) -> Option<&'a T> - where - 'tcx: 'a, - { - let this = self.eval_context_ref(); - this.machine.sync.rwlocks[id].data.as_deref().and_then(|p| p.downcast_ref::<T>()) +impl<'tcx> AllocExtra<'tcx> { + pub fn get_sync<T: 'static>(&self, offset: Size) -> Option<&T> { + self.sync.get(&offset).and_then(|s| s.downcast_ref::<T>()) } +} + +/// We designate an `init`` field in all primitives. +/// If `init` is set to this, we consider the primitive initialized. +pub const LAZY_INIT_COOKIE: u32 = 0xcafe_affe; - /// Eagerly create and initialize a new condvar. - fn condvar_create( +// Public interface to synchronization primitives. Please note that in most +// cases, the function calls are infallible and it is the client's (shim +// implementation's) responsibility to detect and deal with erroneous +// situations. +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Helper for lazily initialized `alloc_extra.sync` data: + /// this forces an immediate init. + fn lazy_sync_init<T: 'static + Copy>( &mut self, - condvar: &MPlaceTy<'tcx>, - offset: u64, - data: Option<Box<dyn Any>>, - ) -> InterpResult<'tcx, CondvarId> { + primitive: &MPlaceTy<'tcx>, + init_offset: Size, + data: T, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - this.create_id(condvar, offset, |ecx| &mut ecx.machine.sync.condvars, Condvar { - data, - ..Default::default() - }) + + let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?; + let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc)?; + alloc_extra.sync.insert(offset, Box::new(data)); + // Mark this as "initialized". + let init_field = primitive.offset(init_offset, this.machine.layouts.u32, this)?; + this.write_scalar_atomic( + Scalar::from_u32(LAZY_INIT_COOKIE), + &init_field, + AtomicWriteOrd::Relaxed, + )?; + interp_ok(()) } - fn condvar_get_or_create_id( + /// Helper for lazily initialized `alloc_extra.sync` data: + /// Checks if the primitive is initialized: + /// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails + /// and stores that in `alloc_extra.sync`. + /// - Otherwise, calls `new_data` to initialize the primitive. + fn lazy_sync_get_data<T: 'static + Copy>( &mut self, - lock: &MPlaceTy<'tcx>, - offset: u64, - initialize_data: impl for<'a> FnOnce( - &'a mut MiriInterpCx<'tcx>, - ) -> InterpResult<'tcx, Option<Box<dyn Any>>>, - ) -> InterpResult<'tcx, CondvarId> { + primitive: &MPlaceTy<'tcx>, + init_offset: Size, + missing_data: impl FnOnce() -> InterpResult<'tcx, T>, + new_data: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>, + ) -> InterpResult<'tcx, T> { let this = self.eval_context_mut(); - this.get_or_create_id( - lock, - offset, - |ecx| &mut ecx.machine.sync.condvars, - |ecx| initialize_data(ecx).map(|data| Condvar { data, ..Default::default() }), - )? - .ok_or_else(|| err_ub_format!("condvar has invalid ID")) - .into() + + // Check if this is already initialized. Needs to be atomic because we can race with another + // thread initializing. Needs to be an RMW operation to ensure we read the *latest* value. + // So we just try to replace MUTEX_INIT_COOKIE with itself. + let init_cookie = Scalar::from_u32(LAZY_INIT_COOKIE); + let init_field = primitive.offset(init_offset, this.machine.layouts.u32, this)?; + let (_init, success) = this + .atomic_compare_exchange_scalar( + &init_field, + &ImmTy::from_scalar(init_cookie, this.machine.layouts.u32), + init_cookie, + AtomicRwOrd::Relaxed, + AtomicReadOrd::Relaxed, + /* can_fail_spuriously */ false, + )? + .to_scalar_pair(); + + if success.to_bool()? { + // If it is initialized, it must be found in the "sync primitive" table, + // or else it has been moved illegally. + let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?; + let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc)?; + if let Some(data) = alloc_extra.get_sync::<T>(offset) { + interp_ok(*data) + } else { + let data = missing_data()?; + alloc_extra.sync.insert(offset, Box::new(data)); + interp_ok(data) + } + } else { + let data = new_data(this)?; + this.lazy_sync_init(primitive, init_offset, data)?; + interp_ok(data) + } } - /// Retrieve the additional data stored for a condvar. - fn condvar_get_data<'a, T: 'static>(&'a mut self, id: CondvarId) -> Option<&'a T> + /// Get the synchronization primitive associated with the given pointer, + /// or initialize a new one. + fn get_sync_or_init<'a, T: 'static>( + &'a mut self, + ptr: Pointer, + new: impl FnOnce(&'a mut MiriMachine<'tcx>) -> InterpResult<'tcx, T>, + ) -> InterpResult<'tcx, &'a T> where 'tcx: 'a, { - let this = self.eval_context_ref(); - this.machine.sync.condvars[id].data.as_deref().and_then(|p| p.downcast_ref::<T>()) + let this = self.eval_context_mut(); + // Ensure there is memory behind this pointer, so that this allocation + // is truly the only place where the data could be stored. + this.check_ptr_access(ptr, Size::from_bytes(1), CheckInAllocMsg::InboundsTest)?; + + let (alloc, offset, _) = this.ptr_get_alloc_id(ptr, 0)?; + let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc)?; + // Due to borrow checker reasons, we have to do the lookup twice. + if alloc_extra.get_sync::<T>(offset).is_none() { + let new = new(machine)?; + alloc_extra.sync.insert(offset, Box::new(new)); + } + interp_ok(alloc_extra.get_sync::<T>(offset).unwrap()) } #[inline] @@ -444,7 +350,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The mutex is completely unlocked. Try transferring ownership // to another thread. if let Some(data_race) = &this.machine.data_race { - mutex.clock.clone_from(&data_race.release_clock(&this.machine.threads)); + data_race.release_clock(&this.machine.threads, |clock| { + mutex.clock.clone_from(clock) + }); } if let Some(thread) = this.machine.sync.mutexes[id].queue.pop_front() { this.unblock_thread(thread, BlockReason::Mutex(id))?; @@ -553,7 +461,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } if let Some(data_race) = &this.machine.data_race { // Add this to the shared-release clock of all concurrent readers. - rwlock.clock_current_readers.join(&data_race.release_clock(&this.machine.threads)); + data_race.release_clock(&this.machine.threads, |clock| { + rwlock.clock_current_readers.join(clock) + }); } // The thread was a reader. If the lock is not held any more, give it to a writer. @@ -632,7 +542,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("rwlock_writer_unlock: {:?} unlocked by {:?}", id, thread); // Record release clock for next lock holder. if let Some(data_race) = &this.machine.data_race { - rwlock.clock_unlocked.clone_from(&*data_race.release_clock(&this.machine.threads)); + data_race.release_clock(&this.machine.threads, |clock| { + rwlock.clock_unlocked.clone_from(clock) + }); } // The thread was a writer. // @@ -764,7 +676,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Each condvar signal happens-before the end of the condvar wake if let Some(data_race) = data_race { - condvar.clock.clone_from(&*data_race.release_clock(&this.machine.threads)); + data_race.release_clock(&this.machine.threads, |clock| condvar.clock.clone_from(clock)); } let Some(waiter) = condvar.waiters.pop_front() else { return interp_ok(false); @@ -837,7 +749,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Each futex-wake happens-before the end of the futex wait if let Some(data_race) = data_race { - futex.clock.clone_from(&*data_race.release_clock(&this.machine.threads)); + data_race.release_clock(&this.machine.threads, |clock| futex.clock.clone_from(clock)); } // Wake up the first thread in the queue that matches any of the bits in the bitset. diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index dcae85109a5..3946cb5ee54 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -50,7 +50,7 @@ pub trait UnblockCallback<'tcx>: VisitProvenance { fn timeout(self: Box<Self>, _ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) -> InterpResult<'tcx>; } -type DynUnblockCallback<'tcx> = Box<dyn UnblockCallback<'tcx> + 'tcx>; +pub type DynUnblockCallback<'tcx> = Box<dyn UnblockCallback<'tcx> + 'tcx>; #[macro_export] macro_rules! callback { @@ -59,7 +59,7 @@ macro_rules! callback { @unblock = |$this:ident| $unblock:block ) => { callback!( - @capture<$tcx, $($lft),*> { $($name: $type),+ } + @capture<$tcx, $($lft),*> { $($name: $type),* } @unblock = |$this| $unblock @timeout = |_this| { unreachable!( @@ -101,7 +101,7 @@ macro_rules! callback { } } - Callback { $($name,)* _phantom: std::marker::PhantomData } + Box::new(Callback { $($name,)* _phantom: std::marker::PhantomData }) }} } @@ -715,11 +715,11 @@ impl<'tcx> ThreadManager<'tcx> { &mut self, reason: BlockReason, timeout: Option<Timeout>, - callback: impl UnblockCallback<'tcx> + 'tcx, + callback: DynUnblockCallback<'tcx>, ) { let state = &mut self.threads[self.active_thread].state; assert!(state.is_enabled()); - *state = ThreadState::Blocked { reason, timeout, callback: Box::new(callback) } + *state = ThreadState::Blocked { reason, timeout, callback } } /// Change the active thread to some enabled thread. @@ -1032,7 +1032,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, reason: BlockReason, timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, - callback: impl UnblockCallback<'tcx> + 'tcx, + callback: DynUnblockCallback<'tcx>, ) { let this = self.eval_context_mut(); let timeout = timeout.map(|(clock, anchor, duration)| { diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 5b1bad28c07..475139a3b51 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -473,14 +473,14 @@ pub fn report_leaks<'tcx>( leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>, MiriAllocBytes>)>, ) { let mut any_pruned = false; - for (id, kind, mut alloc) in leaks { + for (id, kind, alloc) in leaks { let mut title = format!( "memory leaked: {id:?} ({}, size: {:?}, align: {:?})", kind, alloc.size().bytes(), alloc.align.bytes() ); - let Some(backtrace) = alloc.extra.backtrace.take() else { + let Some(backtrace) = alloc.extra.backtrace else { ecx.tcx.dcx().err(title); continue; }; diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index ece76e581f2..9f93f151668 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -129,8 +129,6 @@ pub struct MiriConfig { /// If `Some`, enable the `measureme` profiler, writing results to a file /// with the specified prefix. pub measureme_out: Option<String>, - /// Panic when unsupported functionality is encountered. - pub panic_on_unsupported: bool, /// Which style to use for printing backtraces. pub backtrace_style: BacktraceStyle, /// Which provenance to use for int2ptr casts @@ -183,7 +181,6 @@ impl Default for MiriConfig { track_outdated_loads: false, cmpxchg_weak_failure_rate: 0.8, // 80% measureme_out: None, - panic_on_unsupported: false, backtrace_style: BacktraceStyle::Short, provenance_mode: ProvenanceMode::Default, mute_stdout_stderr: false, @@ -476,7 +473,7 @@ pub fn eval_entry<'tcx>( } // Check for memory leaks. info!("Additional static roots: {:?}", ecx.machine.static_roots); - let leaks = ecx.find_leaked_allocations(&ecx.machine.static_roots); + let leaks = ecx.take_leaked_allocations(|ecx| &ecx.machine.static_roots); if !leaks.is_empty() { report_leaks(&ecx, leaks); tcx.dcx().note("set `MIRIFLAGS=-Zmiri-ignore-leaks` to disable this check"); diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 013bfe03aaf..d35cbf242f5 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -14,7 +14,6 @@ use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::ExportedSymbol; -use rustc_middle::mir; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, MaybeResult, TyAndLayout}; use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy}; use rustc_session::config::CrateType; @@ -224,14 +223,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Evaluates the scalar at the specified path. - fn eval_path(&self, path: &[&str]) -> OpTy<'tcx> { + fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> { let this = self.eval_context_ref(); let instance = resolve_path(*this.tcx, path, Namespace::ValueNS); // We don't give a span -- this isn't actually used directly by the program anyway. - let const_val = this.eval_global(instance).unwrap_or_else(|err| { + this.eval_global(instance).unwrap_or_else(|err| { panic!("failed to evaluate required Rust item: {path:?}\n{err:?}") - }); - const_val.into() + }) } fn eval_path_scalar(&self, path: &[&str]) -> Scalar { let this = self.eval_context_ref(); @@ -949,21 +947,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { crate_name == "std" || crate_name == "std_miri_test" } - /// Handler that should be called when an unsupported foreign item is encountered. - /// This function will either panic within the context of the emulated application - /// or return an error in the Miri process context - fn handle_unsupported_foreign_item(&mut self, error_msg: String) -> InterpResult<'tcx, ()> { - let this = self.eval_context_mut(); - if this.machine.panic_on_unsupported { - // message is slightly different here to make automated analysis easier - let error_msg = format!("unsupported Miri functionality: {error_msg}"); - this.start_panic(error_msg.as_ref(), mir::UnwindAction::Continue)?; - interp_ok(()) - } else { - throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(error_msg)); - } - } - fn check_abi_and_shim_symbol_clash( &mut self, abi: Abi, @@ -1196,6 +1179,21 @@ where throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N) } +/// Check that the number of args is at least the minumim what we expect. +pub fn check_min_arg_count<'a, 'tcx, const N: usize>( + name: &'a str, + args: &'a [OpTy<'tcx>], +) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { + if let Some((ops, _)) = args.split_first_chunk() { + return interp_ok(ops); + } + throw_ub_format!( + "incorrect number of arguments for `{name}`: got {}, expected at least {}", + args.len(), + N + ) +} + pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> { throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( "{name} not available when isolation is enabled", diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index e0d27f21dae..09ec2cb46b0 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -262,6 +262,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let b = this.read_scalar(b)?.to_f32()?; let c = this.read_scalar(c)?.to_f32()?; let fuse: bool = this.machine.rng.get_mut().gen(); + #[allow(clippy::arithmetic_side_effects)] // float ops don't overflow let res = if fuse { // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 a.to_host().mul_add(b.to_host(), c.to_host()).to_soft() @@ -277,6 +278,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let b = this.read_scalar(b)?.to_f64()?; let c = this.read_scalar(c)?.to_f64()?; let fuse: bool = this.machine.rng.get_mut().gen(); + #[allow(clippy::arithmetic_side_effects)] // float ops don't overflow let res = if fuse { // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 a.to_host().mul_add(b.to_host(), c.to_host()).to_soft() diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 330147c8f1c..f089d1e1bcc 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -1,6 +1,5 @@ #![feature(rustc_private)] #![feature(cell_update)] -#![feature(const_option)] #![feature(float_gamma)] #![feature(map_try_insert)] #![feature(never_type)] diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index b9cebcfe9cd..60d096b92f2 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1,6 +1,7 @@ //! Global machine state as well as implementation of the interpreter engine //! `Machine` trait. +use std::any::Any; use std::borrow::Cow; use std::cell::RefCell; use std::collections::hash_map::Entry; @@ -321,7 +322,7 @@ impl ProvenanceExtra { } /// Extra per-allocation data -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct AllocExtra<'tcx> { /// Global state of the borrow tracker, if enabled. pub borrow_tracker: Option<borrow_tracker::AllocState>, @@ -336,11 +337,24 @@ pub struct AllocExtra<'tcx> { /// if this allocation is leakable. The backtrace is not /// pruned yet; that should be done before printing it. pub backtrace: Option<Vec<FrameInfo<'tcx>>>, + /// Synchronization primitives like to attach extra data to particular addresses. We store that + /// inside the relevant allocation, to ensure that everything is removed when the allocation is + /// freed. + /// This maps offsets to synchronization-primitive-specific data. + pub sync: FxHashMap<Size, Box<dyn Any>>, +} + +// We need a `Clone` impl because the machine passes `Allocation` through `Cow`... +// but that should never end up actually cloning our `AllocExtra`. +impl<'tcx> Clone for AllocExtra<'tcx> { + fn clone(&self) -> Self { + panic!("our allocations should never be cloned"); + } } impl VisitProvenance for AllocExtra<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self; + let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _, sync: _ } = self; borrow_tracker.visit_provenance(visit); data_race.visit_provenance(visit); @@ -496,11 +510,6 @@ pub struct MiriMachine<'tcx> { /// `None` means no `Instance` exported under the given name is found. pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>, - /// Whether to raise a panic in the context of the evaluated process when unsupported - /// functionality is encountered. If `false`, an error is propagated in the Miri application context - /// instead (default behavior) - pub(crate) panic_on_unsupported: bool, - /// Equivalent setting as RUST_BACKTRACE on encountering an error. pub(crate) backtrace_style: BacktraceStyle, @@ -667,7 +676,6 @@ impl<'tcx> MiriMachine<'tcx> { profiler, string_cache: Default::default(), exported_symbols_cache: FxHashMap::default(), - panic_on_unsupported: config.panic_on_unsupported, backtrace_style: config.backtrace_style, local_crates, extern_statics: FxHashMap::default(), @@ -807,7 +815,6 @@ impl VisitProvenance for MiriMachine<'_> { profiler: _, string_cache: _, exported_symbols_cache: _, - panic_on_unsupported: _, backtrace_style: _, local_crates: _, rng: _, @@ -1186,7 +1193,13 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { .insert(id, (ecx.machine.current_span(), None)); } - interp_ok(AllocExtra { borrow_tracker, data_race, weak_memory, backtrace }) + interp_ok(AllocExtra { + borrow_tracker, + data_race, + weak_memory, + backtrace, + sync: FxHashMap::default(), + }) } fn adjust_alloc_root_pointer( diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 25afda4edc8..ae2cdaa8d57 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -5,6 +5,7 @@ use rustc_span::{BytePos, Loc, Symbol, hygiene}; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; +use crate::helpers::check_min_arg_count; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -39,11 +40,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let tcx = this.tcx; - let flags = if let Some(flags_op) = args.first() { - this.read_scalar(flags_op)?.to_u64()? - } else { - throw_ub_format!("expected at least 1 argument") - }; + let [flags] = check_min_arg_count("miri_get_backtrace", args)?; + let flags = this.read_scalar(flags)?.to_u64()?; let mut data = Vec::new(); for frame in this.active_thread_stack().iter().rev() { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 78b07f68b44..7fce5b63306 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -83,11 +83,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(Some(body)); } - this.handle_unsupported_foreign_item(format!( + throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!( "can't call foreign function `{link_name}` on OS `{os}`", os = this.tcx.sess.target.os, - ))?; - return interp_ok(None); + ))); } } diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 34e29760da7..e3914640037 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -9,6 +9,7 @@ use std::rc::{Rc, Weak}; use rustc_target::abi::Size; +use crate::helpers::check_min_arg_count; use crate::shims::unix::linux::epoll::EpollReadyEvents; use crate::shims::unix::*; use crate::*; @@ -481,56 +482,62 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let [fd_num, cmd, ..] = args else { - throw_ub_format!( - "incorrect number of arguments for fcntl: got {}, expected at least 2", - args.len() - ); - }; + let [fd_num, cmd] = check_min_arg_count("fcntl", args)?; + let fd_num = this.read_scalar(fd_num)?.to_i32()?; let cmd = this.read_scalar(cmd)?.to_i32()?; + let f_getfd = this.eval_libc_i32("F_GETFD"); + let f_dupfd = this.eval_libc_i32("F_DUPFD"); + let f_dupfd_cloexec = this.eval_libc_i32("F_DUPFD_CLOEXEC"); + // We only support getting the flags for a descriptor. - if cmd == this.eval_libc_i32("F_GETFD") { - // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the - // `FD_CLOEXEC` value without checking if the flag is set for the file because `std` - // always sets this flag when opening a file. However we still need to check that the - // file itself is open. - interp_ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) { - this.eval_libc_i32("FD_CLOEXEC") - } else { - this.fd_not_found()? - })) - } else if cmd == this.eval_libc_i32("F_DUPFD") - || cmd == this.eval_libc_i32("F_DUPFD_CLOEXEC") - { - // Note that we always assume the FD_CLOEXEC flag is set for every open file, in part - // because exec() isn't supported. The F_DUPFD and F_DUPFD_CLOEXEC commands only - // differ in whether the FD_CLOEXEC flag is pre-set on the new file descriptor, - // thus they can share the same implementation here. - let [_, _, start, ..] = args else { - throw_ub_format!( - "incorrect number of arguments for fcntl with cmd=`F_DUPFD`/`F_DUPFD_CLOEXEC`: got {}, expected at least 3", - args.len() - ); - }; - let start = this.read_scalar(start)?.to_i32()?; - - match this.machine.fds.get(fd_num) { - Some(fd) => - interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))), - None => interp_ok(Scalar::from_i32(this.fd_not_found()?)), + match cmd { + cmd if cmd == f_getfd => { + // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the + // `FD_CLOEXEC` value without checking if the flag is set for the file because `std` + // always sets this flag when opening a file. However we still need to check that the + // file itself is open. + interp_ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) { + this.eval_libc_i32("FD_CLOEXEC") + } else { + this.fd_not_found()? + })) } - } else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") { - // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`fcntl`", reject_with)?; - return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); + cmd if cmd == f_dupfd || cmd == f_dupfd_cloexec => { + // Note that we always assume the FD_CLOEXEC flag is set for every open file, in part + // because exec() isn't supported. The F_DUPFD and F_DUPFD_CLOEXEC commands only + // differ in whether the FD_CLOEXEC flag is pre-set on the new file descriptor, + // thus they can share the same implementation here. + let cmd_name = if cmd == f_dupfd { + "fcntl(fd, F_DUPFD, ...)" + } else { + "fcntl(fd, F_DUPFD_CLOEXEC, ...)" + }; + + let [_, _, start] = check_min_arg_count(cmd_name, args)?; + let start = this.read_scalar(start)?.to_i32()?; + + if let Some(fd) = this.machine.fds.get(fd_num) { + interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))) + } else { + interp_ok(Scalar::from_i32(this.fd_not_found()?)) + } } + cmd if this.tcx.sess.target.os == "macos" + && cmd == this.eval_libc_i32("F_FULLFSYNC") => + { + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fcntl`", reject_with)?; + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); + } - this.ffullsync_fd(fd_num) - } else { - throw_unsup_format!("the {:#x} command is not supported for `fcntl`)", cmd); + this.ffullsync_fd(fd_num) + } + cmd => { + throw_unsup_format!("fcntl: unsupported command {cmd:#x}"); + } } } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 908f91a3bd6..7ba98981920 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -292,6 +292,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "pipe2" => { + // Currently this function does not exist on all Unixes, e.g. on macOS. + if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "solaris" | "illumos") { + throw_unsup_format!( + "`pipe2` is not supported on {}", + this.tcx.sess.target.os + ); + } let [pipefd, flags] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.pipe2(pipefd, Some(flags))?; diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index e89dd488a2f..5204e57705a 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -34,11 +34,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_get_name_np" => { let [thread, name, len] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - // FreeBSD's pthread_get_name_np does not return anything. + // FreeBSD's pthread_get_name_np does not return anything + // and uses strlcpy, which truncates the resulting value, + // but always adds a null terminator (except for zero-sized buffers). + // https://github.com/freebsd/freebsd-src/blob/c2d93a803acef634bd0eede6673aeea59e90c277/lib/libthr/thread/thr_info.c#L119-L144 this.pthread_getname_np( this.read_scalar(thread)?, this.read_scalar(name)?, this.read_scalar(len)?, + /* truncate */ true, )?; } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 6c9a2beac2d..4b3ae8e0520 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -13,6 +13,7 @@ use rustc_target::abi::Size; use self::fd::FlockOp; use self::shims::time::system_time_to_duration; +use crate::helpers::check_min_arg_count; use crate::shims::os_str::bytes_to_os_str; use crate::shims::unix::fd::FileDescriptionRef; use crate::shims::unix::*; @@ -433,12 +434,7 @@ fn maybe_sync_file( impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn open(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { - let [path_raw, flag, ..] = args else { - throw_ub_format!( - "incorrect number of arguments for `open`: got {}, expected at least 2", - args.len() - ); - }; + let [path_raw, flag] = check_min_arg_count("open", args)?; let this = self.eval_context_mut(); @@ -492,14 +488,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Get the mode. On macOS, the argument type `mode_t` is actually `u16`, but // C integer promotion rules mean that on the ABI level, it gets passed as `u32` // (see https://github.com/rust-lang/rust/issues/71915). - let mode = if let Some(arg) = args.get(2) { - this.read_scalar(arg)?.to_u32()? - } else { - throw_ub_format!( - "incorrect number of arguments for `open` with `O_CREAT`: got {}, expected at least 3", - args.len() - ); - }; + let [_, _, mode] = check_min_arg_count("open(pathname, O_CREAT, ...)", args)?; + let mode = this.read_scalar(mode)?.to_u32()?; #[cfg(unix)] { diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index b57347abffa..cafc7161d26 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -32,11 +32,13 @@ pub struct EpollEventInstance { events: u32, /// Original data retrieved from `epoll_event` during `epoll_ctl`. data: u64, + /// The release clock associated with this event. + clock: VClock, } impl EpollEventInstance { pub fn new(events: u32, data: u64) -> EpollEventInstance { - EpollEventInstance { events, data } + EpollEventInstance { events, data, clock: Default::default() } } } @@ -92,7 +94,6 @@ pub struct EpollReadyEvents { #[derive(Debug, Default)] struct ReadyList { mapping: RefCell<BTreeMap<(FdId, i32), EpollEventInstance>>, - clock: RefCell<VClock>, } impl EpollReadyEvents { @@ -480,7 +481,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } if timeout == 0 || !ready_list_empty { // If the ready list is not empty, or the timeout is 0, we can return immediately. - blocking_epoll_callback(epfd_value, weak_epfd, dest, &event, this)?; + return_ready_list(epfd_value, weak_epfd, dest, &event, this)?; } else { // Blocking let timeout = match timeout { @@ -508,7 +509,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { event: MPlaceTy<'tcx>, } @unblock = |this| { - blocking_epoll_callback(epfd_value, weak_epfd, &dest, &event, this)?; + return_ready_list(epfd_value, weak_epfd, &dest, &event, this)?; interp_ok(()) } @timeout = |this| { @@ -567,11 +568,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let epoll = epfd.downcast::<Epoll>().unwrap(); - // Synchronize running thread to the epoll ready list. - if let Some(clock) = &this.release_clock() { - epoll.ready_list.clock.borrow_mut().join(clock); - } - if let Some(thread_id) = epoll.thread_id.borrow_mut().pop() { waiter.push(thread_id); }; @@ -627,7 +623,11 @@ fn check_and_update_one_event_interest<'tcx>( if flags != 0 { let epoll_key = (id, epoll_event_interest.fd_num); let ready_list = &mut epoll_event_interest.ready_list.mapping.borrow_mut(); - let event_instance = EpollEventInstance::new(flags, epoll_event_interest.data); + let mut event_instance = EpollEventInstance::new(flags, epoll_event_interest.data); + // If we are tracking data races, remember the current clock so we can sync with it later. + ecx.release_clock(|clock| { + event_instance.clock.clone_from(clock); + }); // Triggers the notification by inserting it to the ready list. ready_list.insert(epoll_key, event_instance); interp_ok(true) @@ -636,8 +636,9 @@ fn check_and_update_one_event_interest<'tcx>( } } -/// Callback function after epoll_wait unblocks -fn blocking_epoll_callback<'tcx>( +/// Stores the ready list of the `epfd` epoll instance into `events` (which must be an array), +/// and the number of returned events into `dest`. +fn return_ready_list<'tcx>( epfd_value: i32, weak_epfd: WeakFileDescriptionRef, dest: &MPlaceTy<'tcx>, @@ -654,9 +655,6 @@ fn blocking_epoll_callback<'tcx>( let ready_list = epoll_file_description.get_ready_list(); - // Synchronize waking thread from the epoll ready list. - ecx.acquire_clock(&ready_list.clock.borrow()); - let mut ready_list = ready_list.mapping.borrow_mut(); let mut num_of_events: i32 = 0; let mut array_iter = ecx.project_array_fields(events)?; @@ -670,6 +668,9 @@ fn blocking_epoll_callback<'tcx>( ], &des.1, )?; + // Synchronize waking thread with the event of interest. + ecx.acquire_clock(&epoll_event_instance.clock); + num_of_events = num_of_events.strict_add(1); } else { break; diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs index 910ab7e90f2..35bc933885c 100644 --- a/src/tools/miri/src/shims/unix/linux/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -140,9 +140,9 @@ impl FileDescription for Event { match self.counter.get().checked_add(num) { Some(new_count @ 0..=MAX_COUNTER) => { // Future `read` calls will synchronize with this write, so update the FD clock. - if let Some(clock) = &ecx.release_clock() { + ecx.release_clock(|clock| { self.clock.borrow_mut().join(clock); - } + }); self.counter.set(new_count); } None | Some(u64::MAX) => diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 4b5f3b6c81b..e73bde1ddb6 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -5,10 +5,16 @@ use self::shims::unix::linux::epoll::EvalContextExt as _; use self::shims::unix::linux::eventfd::EvalContextExt as _; use self::shims::unix::linux::mem::EvalContextExt as _; use self::shims::unix::linux::sync::futex; +use crate::helpers::check_min_arg_count; use crate::machine::{SIGRTMAX, SIGRTMIN}; use crate::shims::unix::*; use crate::*; +// The documentation of glibc complains that the kernel never exposes +// TASK_COMM_LEN through the headers, so it's assumed to always be 16 bytes +// long including a null terminator. +const TASK_COMM_LEN: usize = 16; + pub fn is_dyn_sym(name: &str) -> bool { matches!(name, "statx") } @@ -74,22 +80,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_setname_np" => { let [thread, name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let max_len = 16; let res = this.pthread_setname_np( this.read_scalar(thread)?, this.read_scalar(name)?, - max_len, + TASK_COMM_LEN, )?; + let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }; this.write_scalar(res, dest)?; } "pthread_getname_np" => { let [thread, name, len] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let res = this.pthread_getname_np( - this.read_scalar(thread)?, - this.read_scalar(name)?, - this.read_scalar(len)?, - )?; + // The function's behavior isn't portable between platforms. + // In case of glibc, the length of the output buffer must + // be not shorter than TASK_COMM_LEN. + let len = this.read_scalar(len)?; + let res = if len.to_target_usize(this)? >= TASK_COMM_LEN as u64 + && this.pthread_getname_np( + this.read_scalar(thread)?, + this.read_scalar(name)?, + len, + /* truncate*/ false, + )? { + Scalar::from_u32(0) + } else { + this.eval_libc("ERANGE") + }; this.write_scalar(res, dest)?; } "gettid" => { @@ -110,24 +126,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let sys_getrandom = this.eval_libc("SYS_getrandom").to_target_usize(this)?; let sys_futex = this.eval_libc("SYS_futex").to_target_usize(this)?; + let sys_eventfd2 = this.eval_libc("SYS_eventfd2").to_target_usize(this)?; - if args.is_empty() { - throw_ub_format!( - "incorrect number of arguments for syscall: got 0, expected at least 1" - ); - } - match this.read_target_usize(&args[0])? { + let [op] = check_min_arg_count("syscall", args)?; + match this.read_target_usize(op)? { // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>). - id if id == sys_getrandom => { + num if num == sys_getrandom => { // Used by getrandom 0.1 // The first argument is the syscall id, so skip over it. - let [_, ptr, len, flags, ..] = args else { - throw_ub_format!( - "incorrect number of arguments for `getrandom` syscall: got {}, expected at least 4", - args.len() - ); - }; + let [_, ptr, len, flags] = + check_min_arg_count("syscall(SYS_getrandom, ...)", args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; @@ -140,14 +149,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_target_usize(len, this), dest)?; } // `futex` is used by some synchronization primitives. - id if id == sys_futex => { - futex(this, &args[1..], dest)?; + num if num == sys_futex => { + futex(this, args, dest)?; + } + num if num == sys_eventfd2 => { + let [_, initval, flags] = + check_min_arg_count("syscall(SYS_evetfd2, ...)", args)?; + + let result = this.eventfd(initval, flags)?; + this.write_int(result.to_i32()?, dest)?; } - id => { - this.handle_unsupported_foreign_item(format!( - "can't execute syscall with ID {id}" - ))?; - return interp_ok(EmulateItemResult::AlreadyJumped); + num => { + throw_unsup_format!("syscall: unsupported syscall number {num}"); } } } diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index 5833ec64fc6..941011bfac6 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -1,7 +1,8 @@ +use crate::helpers::check_min_arg_count; use crate::*; /// Implementation of the SYS_futex syscall. -/// `args` is the arguments *after* the syscall number. +/// `args` is the arguments *including* the syscall number. pub fn futex<'tcx>( this: &mut MiriInterpCx<'tcx>, args: &[OpTy<'tcx>], @@ -15,12 +16,7 @@ pub fn futex<'tcx>( // may or may not be left out from the `syscall()` call. // Therefore we don't use `check_arg_count` here, but only check for the // number of arguments to fall within a range. - let [addr, op, val, ..] = args else { - throw_ub_format!( - "incorrect number of arguments for `futex` syscall: got {}, expected at least 3", - args.len() - ); - }; + let [_, addr, op, val] = check_min_arg_count("`syscall(SYS_futex, ...)`", args)?; // The first three arguments (after the syscall number itself) are the same to all futex operations: // (int *addr, int op, int val). @@ -54,24 +50,16 @@ pub fn futex<'tcx>( op if op & !futex_realtime == futex_wait || op & !futex_realtime == futex_wait_bitset => { let wait_bitset = op & !futex_realtime == futex_wait_bitset; - let bitset = if wait_bitset { - let [_, _, _, timeout, uaddr2, bitset, ..] = args else { - throw_ub_format!( - "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT_BITSET`: got {}, expected at least 6", - args.len() - ); - }; + let (timeout, bitset) = if wait_bitset { + let [_, _, _, _, timeout, uaddr2, bitset] = + check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT_BITSET, ...)`", args)?; let _timeout = this.read_pointer(timeout)?; let _uaddr2 = this.read_pointer(uaddr2)?; - this.read_scalar(bitset)?.to_u32()? + (timeout, this.read_scalar(bitset)?.to_u32()?) } else { - if args.len() < 4 { - throw_ub_format!( - "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT`: got {}, expected at least 4", - args.len() - ); - } - u32::MAX + let [_, _, _, _, timeout] = + check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT, ...)`", args)?; + (timeout, u32::MAX) }; if bitset == 0 { @@ -80,7 +68,7 @@ pub fn futex<'tcx>( return interp_ok(()); } - let timeout = this.deref_pointer_as(&args[3], this.libc_ty_layout("timespec"))?; + let timeout = this.deref_pointer_as(timeout, this.libc_ty_layout("timespec"))?; let timeout = if this.ptr_is_null(timeout.ptr())? { None } else { @@ -183,12 +171,8 @@ pub fn futex<'tcx>( // Same as FUTEX_WAKE, but allows you to specify a bitset to select which threads to wake up. op if op == futex_wake || op == futex_wake_bitset => { let bitset = if op == futex_wake_bitset { - let [_, _, _, timeout, uaddr2, bitset, ..] = args else { - throw_ub_format!( - "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAKE_BITSET`: got {}, expected at least 6", - args.len() - ); - }; + let [_, _, _, _, timeout, uaddr2, bitset] = + check_min_arg_count("`syscall(SYS_futex, FUTEX_WAKE_BITSET, ...)`", args)?; let _timeout = this.read_pointer(timeout)?; let _uaddr2 = this.read_pointer(uaddr2)?; this.read_scalar(bitset)?.to_u32()? diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 2751d379dc0..b199992245c 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -164,13 +164,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_setname_np" => { let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + // The real implementation has logic in two places: + // * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200, + // * in kernel at https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/kern/proc_info.c#L3218-L3227. + // + // The function in libc calls the kernel to validate + // the security policies and the input. If all of the requirements + // are met, then the name is set and 0 is returned. Otherwise, if + // the specified name is lomnger than MAXTHREADNAMESIZE, then + // ENAMETOOLONG is returned. + // + // FIXME: the real implementation maybe returns ESRCH if the thread ID is invalid. let thread = this.pthread_self()?; - let max_len = this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?; - let res = this.pthread_setname_np( + let res = if this.pthread_setname_np( thread, this.read_scalar(name)?, - max_len.try_into().unwrap(), - )?; + this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(), + )? { + Scalar::from_u32(0) + } else { + this.eval_libc("ENAMETOOLONG") + }; // Contrary to the manpage, `pthread_setname_np` on macOS still // returns an integer indicating success. this.write_scalar(res, dest)?; @@ -178,10 +193,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_getname_np" => { let [thread, name, len] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let res = this.pthread_getname_np( + + // The function's behavior isn't portable between platforms. + // In case of macOS, a truncated name (due to a too small buffer) + // does not lead to an error. + // + // For details, see the implementation at + // https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1160-L1175. + // The key part is the strlcpy, which truncates the resulting value, + // but always null terminates (except for zero sized buffers). + // + // FIXME: the real implementation returns ESRCH if the thread ID is invalid. + let res = Scalar::from_u32(0); + this.pthread_getname_np( this.read_scalar(thread)?, this.read_scalar(name)?, this.read_scalar(len)?, + /* truncate */ true, )?; this.write_scalar(res, dest)?; } diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index 2f96849d0d2..1df1202442a 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -10,17 +10,42 @@ //! and we do not detect copying of the lock, but macOS doesn't guarantee anything //! in that case either. +use rustc_target::abi::Size; + use crate::*; +#[derive(Copy, Clone)] +enum MacOsUnfairLock { + Poisoned, + Active { id: MutexId }, +} + impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn os_unfair_lock_getid(&mut self, lock_ptr: &OpTy<'tcx>) -> InterpResult<'tcx, MutexId> { + fn os_unfair_lock_get_data( + &mut self, + lock_ptr: &OpTy<'tcx>, + ) -> InterpResult<'tcx, MacOsUnfairLock> { let this = self.eval_context_mut(); let lock = this.deref_pointer(lock_ptr)?; - // os_unfair_lock holds a 32-bit value, is initialized with zero and - // must be assumed to be opaque. Therefore, we can just store our - // internal mutex ID in the structure without anyone noticing. - this.mutex_get_or_create_id(&lock, 0, |_| interp_ok(None)) + this.lazy_sync_get_data( + &lock, + Size::ZERO, // offset for init tracking + || { + // If we get here, due to how we reset things to zero in `os_unfair_lock_unlock`, + // this means the lock was moved while locked. This can happen with a `std` lock, + // but then any future attempt to unlock will just deadlock. In practice, terrible + // things can probably happen if you swap two locked locks, since they'd wake up + // from the wrong queue... we just won't catch all UB of this library API then (we + // would need to store some unique identifer in-memory for this, instead of a static + // LAZY_INIT_COOKIE). This can't be hit via `std::sync::Mutex`. + interp_ok(MacOsUnfairLock::Poisoned) + }, + |ecx| { + let id = ecx.machine.sync.mutex_create(); + interp_ok(MacOsUnfairLock::Active { id }) + }, + ) } } @@ -29,7 +54,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn os_unfair_lock_lock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.os_unfair_lock_getid(lock_op)?; + let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else { + // Trying to get a poisoned lock. Just block forever... + this.block_thread( + BlockReason::Sleep, + None, + callback!( + @capture<'tcx> {} + @unblock = |_this| { + panic!("we shouldn't wake up ever") + } + ), + ); + return interp_ok(()); + }; + if this.mutex_is_locked(id) { if this.mutex_get_owner(id) == this.active_thread() { // Matching the current macOS implementation: abort on reentrant locking. @@ -53,7 +92,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.os_unfair_lock_getid(lock_op)?; + let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else { + // Trying to get a poisoned lock. That never works. + this.write_scalar(Scalar::from_bool(false), dest)?; + return interp_ok(()); + }; + if this.mutex_is_locked(id) { // Contrary to the blocking lock function, this does not check for // reentrancy. @@ -69,7 +113,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn os_unfair_lock_unlock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.os_unfair_lock_getid(lock_op)?; + let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else { + // The lock is poisoned, who knows who owns it... we'll pretend: someone else. + throw_machine_stop!(TerminationInfo::Abort( + "attempted to unlock an os_unfair_lock not owned by the current thread".to_owned() + )); + }; + + // Now, unlock. if this.mutex_unlock(id)?.is_none() { // Matching the current macOS implementation: abort. throw_machine_stop!(TerminationInfo::Abort( @@ -77,32 +128,56 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } + // If the lock is not locked by anyone now, it went quer. + // Reset to zero so that it can be moved and initialized again for the next phase. + if !this.mutex_is_locked(id) { + let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?; + this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?; + } + interp_ok(()) } fn os_unfair_lock_assert_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.os_unfair_lock_getid(lock_op)?; + let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else { + // The lock is poisoned, who knows who owns it... we'll pretend: someone else. + throw_machine_stop!(TerminationInfo::Abort( + "called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned() + )); + }; if !this.mutex_is_locked(id) || this.mutex_get_owner(id) != this.active_thread() { throw_machine_stop!(TerminationInfo::Abort( "called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned() )); } + // The lock is definitely not quiet since we are the owner. + interp_ok(()) } fn os_unfair_lock_assert_not_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.os_unfair_lock_getid(lock_op)?; + let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else { + // The lock is poisoned, who knows who owns it... we'll pretend: someone else. + return interp_ok(()); + }; if this.mutex_is_locked(id) && this.mutex_get_owner(id) == this.active_thread() { throw_machine_stop!(TerminationInfo::Abort( "called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread".to_owned() )); } + // If the lock is not locked by anyone now, it went quer. + // Reset to zero so that it can be moved and initialized again for the next phase. + if !this.mutex_is_locked(id) { + let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?; + this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?; + } + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index c10098f2733..7f3d0f07bdc 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -31,16 +31,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_scalar(name)?, max_len, )?; + let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }; this.write_scalar(res, dest)?; } "pthread_getname_np" => { let [thread, name, len] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + // https://github.com/illumos/illumos-gate/blob/c56822be04b6c157c8b6f2281e47214c3b86f657/usr/src/lib/libc/port/threads/thr.c#L2449-L2480 let res = this.pthread_getname_np( this.read_scalar(thread)?, this.read_scalar(name)?, this.read_scalar(len)?, + /* truncate */ false, )?; + let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }; this.write_scalar(res, dest)?; } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index b05f340861e..a4beaa47baa 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -2,10 +2,42 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_target::abi::Size; +use crate::concurrency::sync::LAZY_INIT_COOKIE; use crate::*; -// pthread_mutexattr_t is either 4 or 8 bytes, depending on the platform. -// We ignore the platform layout and store our own fields: +/// Do a bytewise comparison of the two places, using relaxed atomic reads. This is used to check if +/// a synchronization primitive matches its static initializer value. +/// +/// The reads happen in chunks of 4, so all racing accesses must also use that access size. +fn bytewise_equal_atomic_relaxed<'tcx>( + ecx: &MiriInterpCx<'tcx>, + left: &MPlaceTy<'tcx>, + right: &MPlaceTy<'tcx>, +) -> InterpResult<'tcx, bool> { + let size = left.layout.size; + assert_eq!(size, right.layout.size); + + // We do this in chunks of 4, so that we are okay to race with (sufficiently aligned) + // 4-byte atomic accesses. + assert!(size.bytes() % 4 == 0); + for i in 0..(size.bytes() / 4) { + let offset = Size::from_bytes(i.strict_mul(4)); + let load = |place: &MPlaceTy<'tcx>| { + let byte = place.offset(offset, ecx.machine.layouts.u32, ecx)?; + ecx.read_scalar_atomic(&byte, AtomicReadOrd::Relaxed)?.to_u32() + }; + let left = load(left)?; + let right = load(right)?; + if left != right { + return interp_ok(false); + } + } + + interp_ok(true) +} + +// # pthread_mutexattr_t +// We store some data directly inside the type, ignoring the platform layout: // - kind: i32 #[inline] @@ -49,52 +81,72 @@ fn mutexattr_set_kind<'tcx>( /// field *not* PTHREAD_MUTEX_DEFAULT but this special flag. const PTHREAD_MUTEX_KIND_UNCHANGED: i32 = 0x8000000; +/// Translates the mutex kind from what is stored in pthread_mutexattr_t to our enum. +fn mutexattr_translate_kind<'tcx>( + ecx: &MiriInterpCx<'tcx>, + kind: i32, +) -> InterpResult<'tcx, MutexKind> { + interp_ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) { + MutexKind::Normal + } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") { + MutexKind::ErrorCheck + } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { + MutexKind::Recursive + } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") + || kind == PTHREAD_MUTEX_KIND_UNCHANGED + { + // We check this *last* since PTHREAD_MUTEX_DEFAULT may be numerically equal to one of the + // others, and we want an explicit `mutexattr_settype` to work as expected. + MutexKind::Default + } else { + throw_unsup_format!("unsupported type of mutex: {kind}"); + }) +} + +// # pthread_mutex_t +// We store some data directly inside the type, ignoring the platform layout: +// - init: u32 + /// The mutex kind. #[derive(Debug, Clone, Copy)] -pub enum MutexKind { +enum MutexKind { Normal, Default, Recursive, ErrorCheck, } -#[derive(Debug)] -/// Additional data that we attach with each mutex instance. -pub struct AdditionalMutexData { - /// The mutex kind, used by some mutex implementations like pthreads mutexes. - pub kind: MutexKind, - - /// The address of the mutex. - pub address: u64, +#[derive(Debug, Clone, Copy)] +struct PthreadMutex { + id: MutexId, + kind: MutexKind, } -// pthread_mutex_t is between 4 and 48 bytes, depending on the platform. -// We ignore the platform layout and store our own fields: -// - id: u32 - -fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { - // When adding a new OS, make sure we also support all its static initializers in - // `mutex_kind_from_static_initializer`! +/// To ensure an initialized mutex that was moved somewhere else can be distinguished from +/// a statically initialized mutex that is used the first time, we pick some offset within +/// `pthread_mutex_t` and use it as an "initialized" flag. +fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> { let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" | "freebsd" | "android" => 0, - // macOS stores a signature in the first bytes, so we have to move to offset 4. + // macOS stores a signature in the first bytes, so we move to offset 4. "macos" => 4, os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), }; + let offset = Size::from_bytes(offset); // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once): - // the id must start out as 0. - // FIXME on some platforms (e.g linux) there are more static initializers for - // recursive or error checking mutexes. We should also add thme in this sanity check. + // the `init` field must start out not equal to INIT_COOKIE. static SANITY: AtomicBool = AtomicBool::new(false); if !SANITY.swap(true, Ordering::Relaxed) { let check_static_initializer = |name| { let static_initializer = ecx.eval_path(&["libc", name]); - let id_field = static_initializer - .offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx) - .unwrap(); - let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap(); - assert_eq!(id, 0, "{name} is incompatible with our pthread_mutex layout: id is not 0"); + let init_field = + static_initializer.offset(offset, ecx.machine.layouts.u32, ecx).unwrap(); + let init = ecx.read_scalar(&init_field).unwrap().to_u32().unwrap(); + assert_ne!( + init, LAZY_INIT_COOKIE, + "{name} is incompatible with our initialization cookie" + ); }; check_static_initializer("PTHREAD_MUTEX_INITIALIZER"); @@ -120,42 +172,33 @@ fn mutex_create<'tcx>( ecx: &mut MiriInterpCx<'tcx>, mutex_ptr: &OpTy<'tcx>, kind: MutexKind, -) -> InterpResult<'tcx> { +) -> InterpResult<'tcx, PthreadMutex> { let mutex = ecx.deref_pointer(mutex_ptr)?; - let address = mutex.ptr().addr().bytes(); - let data = Box::new(AdditionalMutexData { address, kind }); - ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, Some(data))?; - interp_ok(()) + let id = ecx.machine.sync.mutex_create(); + let data = PthreadMutex { id, kind }; + ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data)?; + interp_ok(data) } /// Returns the `MutexId` of the mutex stored at `mutex_op`. /// /// `mutex_get_id` will also check if the mutex has been moved since its first use and /// return an error if it has. -fn mutex_get_id<'tcx>( - ecx: &mut MiriInterpCx<'tcx>, +fn mutex_get_data<'tcx, 'a>( + ecx: &'a mut MiriInterpCx<'tcx>, mutex_ptr: &OpTy<'tcx>, -) -> InterpResult<'tcx, MutexId> { +) -> InterpResult<'tcx, PthreadMutex> { let mutex = ecx.deref_pointer(mutex_ptr)?; - let address = mutex.ptr().addr().bytes(); - - let id = ecx.mutex_get_or_create_id(&mutex, mutex_id_offset(ecx)?, |ecx| { - // This is called if a static initializer was used and the lock has not been assigned - // an ID yet. We have to determine the mutex kind from the static initializer. - let kind = mutex_kind_from_static_initializer(ecx, &mutex)?; - - interp_ok(Some(Box::new(AdditionalMutexData { kind, address }))) - })?; - - // Check that the mutex has not been moved since last use. - let data = ecx - .mutex_get_data::<AdditionalMutexData>(id) - .expect("data should always exist for pthreads"); - if data.address != address { - throw_ub_format!("pthread_mutex_t can't be moved after first use") - } - - interp_ok(id) + ecx.lazy_sync_get_data( + &mutex, + mutex_init_offset(ecx)?, + || throw_ub_format!("`pthread_mutex_t` can't be moved after first use"), + |ecx| { + let kind = mutex_kind_from_static_initializer(ecx, &mutex)?; + let id = ecx.machine.sync.mutex_create(); + interp_ok(PthreadMutex { id, kind }) + }, + ) } /// Returns the kind of a static initializer. @@ -163,107 +206,86 @@ fn mutex_kind_from_static_initializer<'tcx>( ecx: &MiriInterpCx<'tcx>, mutex: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, MutexKind> { - interp_ok(match &*ecx.tcx.sess.target.os { - // Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT. - "linux" => { - let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; - let kind_place = - mutex.offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx)?; - let kind = ecx.read_scalar(&kind_place)?.to_i32()?; - // Here we give PTHREAD_MUTEX_DEFAULT priority so that - // PTHREAD_MUTEX_INITIALIZER behaves like `pthread_mutex_init` with a NULL argument. - if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") { - MutexKind::Default - } else { - mutex_translate_kind(ecx, kind)? - } - } - _ => MutexKind::Default, - }) -} + // All the static initializers recognized here *must* be checked in `mutex_init_offset`! + let is_initializer = + |name| bytewise_equal_atomic_relaxed(ecx, mutex, &ecx.eval_path(&["libc", name])); -fn mutex_translate_kind<'tcx>( - ecx: &MiriInterpCx<'tcx>, - kind: i32, -) -> InterpResult<'tcx, MutexKind> { - interp_ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) { - MutexKind::Normal - } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") { - MutexKind::ErrorCheck - } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { - MutexKind::Recursive - } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") - || kind == PTHREAD_MUTEX_KIND_UNCHANGED - { - // We check this *last* since PTHREAD_MUTEX_DEFAULT may be numerically equal to one of the - // others, and we want an explicit `mutexattr_settype` to work as expected. - MutexKind::Default - } else { - throw_unsup_format!("unsupported type of mutex: {kind}"); - }) + // PTHREAD_MUTEX_INITIALIZER is recognized on all targets. + if is_initializer("PTHREAD_MUTEX_INITIALIZER")? { + return interp_ok(MutexKind::Default); + } + // Support additional platform-specific initializers. + match &*ecx.tcx.sess.target.os { + "linux" => + if is_initializer("PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP")? { + return interp_ok(MutexKind::Recursive); + } else if is_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP")? { + return interp_ok(MutexKind::ErrorCheck); + }, + _ => {} + } + throw_unsup_format!("unsupported static initializer used for `pthread_mutex_t`"); } -// pthread_rwlock_t is between 4 and 56 bytes, depending on the platform. -// We ignore the platform layout and store our own fields: -// - id: u32 +// # pthread_rwlock_t +// We store some data directly inside the type, ignoring the platform layout: +// - init: u32 -#[derive(Debug)] -/// Additional data that we attach with each rwlock instance. -pub struct AdditionalRwLockData { - /// The address of the rwlock. - pub address: u64, +#[derive(Debug, Copy, Clone)] +struct PthreadRwLock { + id: RwLockId, } -fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { +fn rwlock_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> { let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" | "freebsd" | "android" => 0, - // macOS stores a signature in the first bytes, so we have to move to offset 4. + // macOS stores a signature in the first bytes, so we move to offset 4. "macos" => 4, os => throw_unsup_format!("`pthread_rwlock` is not supported on {os}"), }; + let offset = Size::from_bytes(offset); // Sanity-check this against PTHREAD_RWLOCK_INITIALIZER (but only once): - // the id must start out as 0. + // the `init` field must start out not equal to LAZY_INIT_COOKIE. static SANITY: AtomicBool = AtomicBool::new(false); if !SANITY.swap(true, Ordering::Relaxed) { let static_initializer = ecx.eval_path(&["libc", "PTHREAD_RWLOCK_INITIALIZER"]); - let id_field = static_initializer - .offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx) - .unwrap(); - let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap(); - assert_eq!( - id, 0, - "PTHREAD_RWLOCK_INITIALIZER is incompatible with our pthread_rwlock layout: id is not 0" + let init_field = static_initializer.offset(offset, ecx.machine.layouts.u32, ecx).unwrap(); + let init = ecx.read_scalar(&init_field).unwrap().to_u32().unwrap(); + assert_ne!( + init, LAZY_INIT_COOKIE, + "PTHREAD_RWLOCK_INITIALIZER is incompatible with our initialization cookie" ); } interp_ok(offset) } -fn rwlock_get_id<'tcx>( +fn rwlock_get_data<'tcx>( ecx: &mut MiriInterpCx<'tcx>, rwlock_ptr: &OpTy<'tcx>, -) -> InterpResult<'tcx, RwLockId> { +) -> InterpResult<'tcx, PthreadRwLock> { let rwlock = ecx.deref_pointer(rwlock_ptr)?; - let address = rwlock.ptr().addr().bytes(); - - let id = ecx.rwlock_get_or_create_id(&rwlock, rwlock_id_offset(ecx)?, |_| { - interp_ok(Some(Box::new(AdditionalRwLockData { address }))) - })?; - - // Check that the rwlock has not been moved since last use. - let data = ecx - .rwlock_get_data::<AdditionalRwLockData>(id) - .expect("data should always exist for pthreads"); - if data.address != address { - throw_ub_format!("pthread_rwlock_t can't be moved after first use") - } - - interp_ok(id) + ecx.lazy_sync_get_data( + &rwlock, + rwlock_init_offset(ecx)?, + || throw_ub_format!("`pthread_rwlock_t` can't be moved after first use"), + |ecx| { + if !bytewise_equal_atomic_relaxed( + ecx, + &rwlock, + &ecx.eval_path(&["libc", "PTHREAD_RWLOCK_INITIALIZER"]), + )? { + throw_unsup_format!("unsupported static initializer used for `pthread_rwlock_t`"); + } + let id = ecx.machine.sync.rwlock_create(); + interp_ok(PthreadRwLock { id }) + }, + ) } -// pthread_condattr_t. -// We ignore the platform layout and store our own fields: +// # pthread_condattr_t +// We store some data directly inside the type, ignoring the platform layout: // - clock: i32 #[inline] @@ -288,19 +310,6 @@ fn condattr_get_clock_id<'tcx>( .to_i32() } -fn cond_translate_clock_id<'tcx>( - ecx: &MiriInterpCx<'tcx>, - raw_id: i32, -) -> InterpResult<'tcx, ClockId> { - interp_ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") { - ClockId::Realtime - } else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") { - ClockId::Monotonic - } else { - throw_unsup_format!("unsupported clock id: {raw_id}"); - }) -} - fn condattr_set_clock_id<'tcx>( ecx: &mut MiriInterpCx<'tcx>, attr_ptr: &OpTy<'tcx>, @@ -315,30 +324,43 @@ fn condattr_set_clock_id<'tcx>( ) } -// pthread_cond_t can be only 4 bytes in size, depending on the platform. -// We ignore the platform layout and store our own fields: -// - id: u32 +/// Translates the clock from what is stored in pthread_condattr_t to our enum. +fn condattr_translate_clock_id<'tcx>( + ecx: &MiriInterpCx<'tcx>, + raw_id: i32, +) -> InterpResult<'tcx, ClockId> { + interp_ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") { + ClockId::Realtime + } else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") { + ClockId::Monotonic + } else { + throw_unsup_format!("unsupported clock id: {raw_id}"); + }) +} + +// # pthread_cond_t +// We store some data directly inside the type, ignoring the platform layout: +// - init: u32 -fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { +fn cond_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> { let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" | "freebsd" | "android" => 0, - // macOS stores a signature in the first bytes, so we have to move to offset 4. + // macOS stores a signature in the first bytes, so we move to offset 4. "macos" => 4, os => throw_unsup_format!("`pthread_cond` is not supported on {os}"), }; + let offset = Size::from_bytes(offset); // Sanity-check this against PTHREAD_COND_INITIALIZER (but only once): - // the id must start out as 0. + // the `init` field must start out not equal to LAZY_INIT_COOKIE. static SANITY: AtomicBool = AtomicBool::new(false); if !SANITY.swap(true, Ordering::Relaxed) { let static_initializer = ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]); - let id_field = static_initializer - .offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx) - .unwrap(); - let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap(); - assert_eq!( - id, 0, - "PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: id is not 0" + let init_field = static_initializer.offset(offset, ecx.machine.layouts.u32, ecx).unwrap(); + let init = ecx.read_scalar(&init_field).unwrap().to_u32().unwrap(); + assert_ne!( + init, LAZY_INIT_COOKIE, + "PTHREAD_COND_INITIALIZER is incompatible with our initialization cookie" ); } @@ -351,36 +373,46 @@ enum ClockId { Monotonic, } -#[derive(Debug)] -/// Additional data that we attach with each cond instance. -struct AdditionalCondData { - /// The address of the cond. - address: u64, - - /// The clock id of the cond. - clock_id: ClockId, +#[derive(Debug, Copy, Clone)] +struct PthreadCondvar { + id: CondvarId, + clock: ClockId, } -fn cond_get_id<'tcx>( +fn cond_create<'tcx>( ecx: &mut MiriInterpCx<'tcx>, cond_ptr: &OpTy<'tcx>, -) -> InterpResult<'tcx, CondvarId> { + clock: ClockId, +) -> InterpResult<'tcx, PthreadCondvar> { let cond = ecx.deref_pointer(cond_ptr)?; - let address = cond.ptr().addr().bytes(); - let id = ecx.condvar_get_or_create_id(&cond, cond_id_offset(ecx)?, |_ecx| { - // This used the static initializer. The clock there is always CLOCK_REALTIME. - interp_ok(Some(Box::new(AdditionalCondData { address, clock_id: ClockId::Realtime }))) - })?; - - // Check that the mutex has not been moved since last use. - let data = ecx - .condvar_get_data::<AdditionalCondData>(id) - .expect("data should always exist for pthreads"); - if data.address != address { - throw_ub_format!("pthread_cond_t can't be moved after first use") - } + let id = ecx.machine.sync.condvar_create(); + let data = PthreadCondvar { id, clock }; + ecx.lazy_sync_init(&cond, cond_init_offset(ecx)?, data)?; + interp_ok(data) +} - interp_ok(id) +fn cond_get_data<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + cond_ptr: &OpTy<'tcx>, +) -> InterpResult<'tcx, PthreadCondvar> { + let cond = ecx.deref_pointer(cond_ptr)?; + ecx.lazy_sync_get_data( + &cond, + cond_init_offset(ecx)?, + || throw_ub_format!("`pthread_cond_t` can't be moved after first use"), + |ecx| { + if !bytewise_equal_atomic_relaxed( + ecx, + &cond, + &ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]), + )? { + throw_unsup_format!("unsupported static initializer used for `pthread_cond_t`"); + } + // This used the static initializer. The clock there is always CLOCK_REALTIME. + let id = ecx.machine.sync.condvar_create(); + interp_ok(PthreadCondvar { id, clock: ClockId::Realtime }) + }, + ) } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -453,7 +485,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let kind = if this.ptr_is_null(attr)? { MutexKind::Default } else { - mutex_translate_kind(this, mutexattr_get_kind(this, attr_op)?)? + mutexattr_translate_kind(this, mutexattr_get_kind(this, attr_op)?)? }; mutex_create(this, mutex_op, kind)?; @@ -468,20 +500,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = mutex_get_id(this, mutex_op)?; - let kind = this - .mutex_get_data::<AdditionalMutexData>(id) - .expect("data should always exist for pthread mutexes") - .kind; + let mutex = mutex_get_data(this, mutex_op)?; - let ret = if this.mutex_is_locked(id) { - let owner_thread = this.mutex_get_owner(id); + let ret = if this.mutex_is_locked(mutex.id) { + let owner_thread = this.mutex_get_owner(mutex.id); if owner_thread != this.active_thread() { - this.mutex_enqueue_and_block(id, Some((Scalar::from_i32(0), dest.clone()))); + this.mutex_enqueue_and_block(mutex.id, Some((Scalar::from_i32(0), dest.clone()))); return interp_ok(()); } else { // Trying to acquire the same mutex again. - match kind { + match mutex.kind { MutexKind::Default => throw_ub_format!( "trying to acquire default mutex already locked by the current thread" @@ -489,14 +517,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock), MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"), MutexKind::Recursive => { - this.mutex_lock(id); + this.mutex_lock(mutex.id); 0 } } } } else { // The mutex is unlocked. Let's lock it. - this.mutex_lock(id); + this.mutex_lock(mutex.id); 0 }; this.write_scalar(Scalar::from_i32(ret), dest)?; @@ -506,29 +534,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = mutex_get_id(this, mutex_op)?; - let kind = this - .mutex_get_data::<AdditionalMutexData>(id) - .expect("data should always exist for pthread mutexes") - .kind; + let mutex = mutex_get_data(this, mutex_op)?; - interp_ok(Scalar::from_i32(if this.mutex_is_locked(id) { - let owner_thread = this.mutex_get_owner(id); + interp_ok(Scalar::from_i32(if this.mutex_is_locked(mutex.id) { + let owner_thread = this.mutex_get_owner(mutex.id); if owner_thread != this.active_thread() { this.eval_libc_i32("EBUSY") } else { - match kind { + match mutex.kind { MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck => this.eval_libc_i32("EBUSY"), MutexKind::Recursive => { - this.mutex_lock(id); + this.mutex_lock(mutex.id); 0 } } } } else { // The mutex is unlocked. Let's lock it. - this.mutex_lock(id); + this.mutex_lock(mutex.id); 0 })) } @@ -536,20 +560,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = mutex_get_id(this, mutex_op)?; - let kind = this - .mutex_get_data::<AdditionalMutexData>(id) - .expect("data should always exist for pthread mutexes") - .kind; + let mutex = mutex_get_data(this, mutex_op)?; - if let Some(_old_locked_count) = this.mutex_unlock(id)? { + if let Some(_old_locked_count) = this.mutex_unlock(mutex.id)? { // The mutex was locked by the current thread. interp_ok(Scalar::from_i32(0)) } else { // The mutex was locked by another thread or not locked at all. See // the “Unlock When Not Owner” column in // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_unlock.html. - match kind { + match mutex.kind { MutexKind::Default => throw_ub_format!( "unlocked a default mutex that was not locked by the current thread" @@ -569,9 +589,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reading the field also has the side-effect that we detect double-`destroy` // since we make the field unint below. - let id = mutex_get_id(this, mutex_op)?; + let mutex = mutex_get_data(this, mutex_op)?; - if this.mutex_is_locked(id) { + if this.mutex_is_locked(mutex.id) { throw_ub_format!("destroyed a locked mutex"); } @@ -591,7 +611,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = rwlock_get_id(this, rwlock_op)?; + let id = rwlock_get_data(this, rwlock_op)?.id; if this.rwlock_is_write_locked(id) { this.rwlock_enqueue_and_block_reader(id, Scalar::from_i32(0), dest.clone()); @@ -606,7 +626,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = rwlock_get_id(this, rwlock_op)?; + let id = rwlock_get_data(this, rwlock_op)?.id; if this.rwlock_is_write_locked(id) { interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) @@ -623,7 +643,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = rwlock_get_id(this, rwlock_op)?; + let id = rwlock_get_data(this, rwlock_op)?.id; if this.rwlock_is_locked(id) { // Note: this will deadlock if the lock is already locked by this @@ -650,7 +670,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = rwlock_get_id(this, rwlock_op)?; + let id = rwlock_get_data(this, rwlock_op)?.id; if this.rwlock_is_locked(id) { interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) @@ -663,7 +683,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); - let id = rwlock_get_id(this, rwlock_op)?; + let id = rwlock_get_data(this, rwlock_op)?.id; #[allow(clippy::if_same_then_else)] if this.rwlock_reader_unlock(id)? || this.rwlock_writer_unlock(id)? { @@ -678,7 +698,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reading the field also has the side-effect that we detect double-`destroy` // since we make the field unint below. - let id = rwlock_get_id(this, rwlock_op)?; + let id = rwlock_get_data(this, rwlock_op)?.id; if this.rwlock_is_locked(id) { throw_ub_format!("destroyed a locked rwlock"); @@ -773,29 +793,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { condattr_get_clock_id(this, attr_op)? }; - let clock_id = cond_translate_clock_id(this, clock_id)?; - - let cond = this.deref_pointer(cond_op)?; - let address = cond.ptr().addr().bytes(); - this.condvar_create( - &cond, - cond_id_offset(this)?, - Some(Box::new(AdditionalCondData { address, clock_id })), - )?; + let clock_id = condattr_translate_clock_id(this, clock_id)?; + + cond_create(this, cond_op, clock_id)?; interp_ok(()) } fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); - let id = cond_get_id(this, cond_op)?; + let id = cond_get_data(this, cond_op)?.id; this.condvar_signal(id)?; interp_ok(()) } fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); - let id = cond_get_id(this, cond_op)?; + let id = cond_get_data(this, cond_op)?.id; while this.condvar_signal(id)? {} interp_ok(()) } @@ -808,11 +822,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = cond_get_id(this, cond_op)?; - let mutex_id = mutex_get_id(this, mutex_op)?; + let data = cond_get_data(this, cond_op)?; + let mutex_id = mutex_get_data(this, mutex_op)?.id; this.condvar_wait( - id, + data.id, mutex_id, None, // no timeout Scalar::from_i32(0), @@ -832,14 +846,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = cond_get_id(this, cond_op)?; - let mutex_id = mutex_get_id(this, mutex_op)?; + let data = cond_get_data(this, cond_op)?; + let mutex_id = mutex_get_data(this, mutex_op)?.id; // Extract the timeout. - let clock_id = this - .condvar_get_data::<AdditionalCondData>(id) - .expect("additional data should always be present for pthreads") - .clock_id; let duration = match this .read_timespec(&this.deref_pointer_as(abstime_op, this.libc_ty_layout("timespec"))?)? { @@ -850,7 +860,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(()); } }; - let timeout_clock = match clock_id { + let timeout_clock = match data.clock { ClockId::Realtime => { this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; TimeoutClock::RealTime @@ -859,7 +869,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; this.condvar_wait( - id, + data.id, mutex_id, Some((timeout_clock, TimeoutAnchor::Absolute, duration)), Scalar::from_i32(0), @@ -875,7 +885,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reading the field also has the side-effect that we detect double-`destroy` // since we make the field unint below. - let id = cond_get_id(this, cond_op)?; + let id = cond_get_data(this, cond_op)?.id; if this.condvar_is_awaited(id) { throw_ub_format!("destroying an awaited conditional variable"); } diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 5515524f2f1..7f97afc8e4b 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -63,38 +63,41 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_uint(thread_id.to_u32(), this.libc_ty_layout("pthread_t").size)) } - /// Set the name of the current thread. `max_name_len` is the maximal length of the name - /// including the null terminator. + /// Set the name of the specified thread. If the name including the null terminator + /// is longer than `name_max_len`, then `false` is returned. fn pthread_setname_np( &mut self, thread: Scalar, name: Scalar, - max_name_len: usize, - ) -> InterpResult<'tcx, Scalar> { + name_max_len: usize, + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?; let thread = ThreadId::try_from(thread).unwrap(); let name = name.to_pointer(this)?; - let name = this.read_c_str(name)?.to_owned(); // Comparing with `>=` to account for null terminator. - if name.len() >= max_name_len { - return interp_ok(this.eval_libc("ERANGE")); + if name.len() >= name_max_len { + return interp_ok(false); } this.set_thread_name(thread, name); - interp_ok(Scalar::from_u32(0)) + interp_ok(true) } + /// Get the name of the specified thread. If the thread name doesn't fit + /// the buffer, then if `truncate` is set the truncated name is written out, + /// otherwise `false` is returned. fn pthread_getname_np( &mut self, thread: Scalar, name_out: Scalar, len: Scalar, - ) -> InterpResult<'tcx, Scalar> { + truncate: bool, + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?; @@ -104,9 +107,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME: we should use the program name if the thread name is not set let name = this.get_thread_name(thread).unwrap_or(b"<unnamed>").to_owned(); - let (success, _written) = this.write_c_str(&name, name_out, len)?; + let name = match truncate { + true => &name[..name.len().min(len.try_into().unwrap_or(usize::MAX).saturating_sub(1))], + false => &name, + }; + + let (success, _written) = this.write_c_str(name, name_out, len)?; - interp_ok(if success { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }) + interp_ok(success) } fn sched_yield(&mut self) -> InterpResult<'tcx, ()> { diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index faa54c6a75e..d0eba1eacd1 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -163,7 +163,7 @@ impl FileDescription for AnonSocket { } else { // Blocking socketpair with writer and empty buffer. // FIXME: blocking is currently not supported - throw_unsup_format!("socketpair read: blocking isn't supported yet"); + throw_unsup_format!("socketpair/pipe/pipe2 read: blocking isn't supported yet"); } } } @@ -230,13 +230,13 @@ impl FileDescription for AnonSocket { return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); } else { // Blocking socketpair with a full buffer. - throw_unsup_format!("socketpair write: blocking isn't supported yet"); + throw_unsup_format!("socketpair/pipe/pipe2 write: blocking isn't supported yet"); } } // Remember this clock so `read` can synchronize with us. - if let Some(clock) = &ecx.release_clock() { + ecx.release_clock(|clock| { writebuf.clock.join(clock); - } + }); // Do full write / partial write based on the space available. let actual_write_size = len.min(available_space); let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; @@ -267,21 +267,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let domain = this.read_scalar(domain)?.to_i32()?; - let mut type_ = this.read_scalar(type_)?.to_i32()?; + let mut flags = this.read_scalar(type_)?.to_i32()?; let protocol = this.read_scalar(protocol)?.to_i32()?; let sv = this.deref_pointer(sv)?; let mut is_sock_nonblock = false; - // Parse and remove the type flags that we support. - // SOCK_NONBLOCK only exists on Linux. + // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so + // if there is anything left at the end, that's an unsupported flag. if this.tcx.sess.target.os == "linux" { - if type_ & this.eval_libc_i32("SOCK_NONBLOCK") == this.eval_libc_i32("SOCK_NONBLOCK") { + // SOCK_NONBLOCK only exists on Linux. + let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK"); + let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC"); + if flags & sock_nonblock == sock_nonblock { is_sock_nonblock = true; - type_ &= !(this.eval_libc_i32("SOCK_NONBLOCK")); + flags &= !sock_nonblock; } - if type_ & this.eval_libc_i32("SOCK_CLOEXEC") == this.eval_libc_i32("SOCK_CLOEXEC") { - type_ &= !(this.eval_libc_i32("SOCK_CLOEXEC")); + if flags & sock_cloexec == sock_cloexec { + flags &= !sock_cloexec; } } @@ -294,11 +297,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { and AF_LOCAL are allowed", domain ); - } else if type_ != this.eval_libc_i32("SOCK_STREAM") { + } else if flags != this.eval_libc_i32("SOCK_STREAM") { throw_unsup_format!( "socketpair: type {:#x} is unsupported, only SOCK_STREAM, \ SOCK_CLOEXEC and SOCK_NONBLOCK are allowed", - type_ + flags ); } else if protocol != 0 { throw_unsup_format!( @@ -347,14 +350,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let pipefd = this.deref_pointer_as(pipefd, this.machine.layouts.i32)?; - let flags = match flags { + let mut flags = match flags { Some(flags) => this.read_scalar(flags)?.to_i32()?, None => 0, }; - // As usual we ignore CLOEXEC. let cloexec = this.eval_libc_i32("O_CLOEXEC"); - if flags != 0 && flags != cloexec { + let o_nonblock = this.eval_libc_i32("O_NONBLOCK"); + + // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so + // if there is anything left at the end, that's an unsupported flag. + let mut is_nonblock = false; + if flags & o_nonblock == o_nonblock { + is_nonblock = true; + flags &= !o_nonblock; + } + // As usual we ignore CLOEXEC. + if flags & cloexec == cloexec { + flags &= !cloexec; + } + if flags != 0 { throw_unsup_format!("unsupported flags in `pipe2`"); } @@ -365,13 +380,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { readbuf: Some(RefCell::new(Buffer::new())), peer_fd: OnceCell::new(), peer_lost_data: Cell::new(false), - is_nonblock: false, + is_nonblock, }); let fd1 = fds.new_ref(AnonSocket { readbuf: None, peer_fd: OnceCell::new(), peer_lost_data: Cell::new(false), - is_nonblock: false, + is_nonblock, }); // Make the file descriptions point to each other. diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 6755c23039e..f8861085fe5 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -5,15 +5,35 @@ use rustc_target::abi::Size; use crate::concurrency::init_once::InitOnceStatus; use crate::*; +#[derive(Copy, Clone)] +struct WindowsInitOnce { + id: InitOnceId, +} + impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Windows sync primitives are pointer sized. // We only use the first 4 bytes for the id. - fn init_once_get_id(&mut self, init_once_ptr: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> { + fn init_once_get_data( + &mut self, + init_once_ptr: &OpTy<'tcx>, + ) -> InterpResult<'tcx, WindowsInitOnce> { let this = self.eval_context_mut(); + let init_once = this.deref_pointer(init_once_ptr)?; - this.init_once_get_or_create_id(&init_once, 0) + let init_offset = Size::ZERO; + + this.lazy_sync_get_data( + &init_once, + init_offset, + || throw_ub_format!("`INIT_ONCE` can't be moved after first use"), + |this| { + // TODO: check that this is still all-zero. + let id = this.machine.sync.init_once_create(); + interp_ok(WindowsInitOnce { id }) + }, + ) } /// Returns `true` if we were succssful, `false` if we would block. @@ -55,7 +75,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.init_once_get_id(init_once_op)?; + let id = this.init_once_get_data(init_once_op)?.id; let flags = this.read_scalar(flags_op)?.to_u32()?; let pending_place = this.deref_pointer(pending_op)?; let context = this.read_pointer(context_op)?; @@ -101,7 +121,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.init_once_get_id(init_once_op)?; + let id = this.init_once_get_data(init_once_op)?.id; let flags = this.read_scalar(flags_op)?.to_u32()?; let context = this.read_pointer(context_op)?; diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.rs new file mode 100644 index 00000000000..84061439334 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.rs @@ -0,0 +1,13 @@ +//@only-target: darwin + +use std::cell::UnsafeCell; + +fn main() { + let lock = UnsafeCell::new(libc::OS_UNFAIR_LOCK_INIT); + + unsafe { libc::os_unfair_lock_lock(lock.get()) }; + let lock = lock; + // This needs to either error or deadlock. + unsafe { libc::os_unfair_lock_lock(lock.get()) }; + //~^ error: deadlock +} diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.stderr new file mode 100644 index 00000000000..f043c7074f0 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.stderr @@ -0,0 +1,13 @@ +error: deadlock: the evaluated program deadlocked + --> tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.rs:LL:CC + | +LL | unsafe { libc::os_unfair_lock_lock(lock.get()) }; + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `main` at tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.init.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.init.stderr index 6e90c490a23..9a8ddc0b523 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.init.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.init.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: pthread_cond_t can't be moved after first use +error: Undefined Behavior: `pthread_cond_t` can't be moved after first use --> tests/fail-dep/concurrency/libc_pthread_cond_move.rs:LL:CC | LL | libc::pthread_cond_destroy(cond2.as_mut_ptr()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_cond_t can't be moved after first use + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `pthread_cond_t` can't be moved after first use | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.rs index 8fd0caac751..4db904ab5e2 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.rs @@ -18,7 +18,7 @@ fn check() { // move pthread_cond_t let mut cond2 = cond; - libc::pthread_cond_destroy(cond2.as_mut_ptr()); //~[init] ERROR: pthread_cond_t can't be moved after first use + libc::pthread_cond_destroy(cond2.as_mut_ptr()); //~[init] ERROR: can't be moved after first use } } @@ -32,6 +32,6 @@ fn check() { // move pthread_cond_t let mut cond2 = cond; - libc::pthread_cond_destroy(&mut cond2 as *mut _); //~[static_initializer] ERROR: pthread_cond_t can't be moved after first use + libc::pthread_cond_destroy(&mut cond2 as *mut _); //~[static_initializer] ERROR: can't be moved after first use } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.static_initializer.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.static_initializer.stderr index ba726ac7f38..8a7c0dee127 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.static_initializer.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_move.static_initializer.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: pthread_cond_t can't be moved after first use +error: Undefined Behavior: `pthread_cond_t` can't be moved after first use --> tests/fail-dep/concurrency/libc_pthread_cond_move.rs:LL:CC | LL | libc::pthread_cond_destroy(&mut cond2 as *mut _); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_cond_t can't be moved after first use + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `pthread_cond_t` can't be moved after first use | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs index d4a9f076bfd..818a27fe66f 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs @@ -12,8 +12,9 @@ extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { fn main() { unsafe { let mut native: libc::pthread_t = mem::zeroed(); - let attr: libc::pthread_attr_t = mem::zeroed(); - // assert_eq!(libc::pthread_attr_init(&mut attr), 0); FIXME: this function is not yet implemented. - assert_eq!(libc::pthread_create(&mut native, &attr, thread_start, ptr::null_mut()), 0); + assert_eq!( + libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + 0 + ); } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs index d4accdba5d7..520bc9572f8 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs @@ -12,12 +12,13 @@ extern "C" fn thread_start() -> *mut libc::c_void { fn main() { unsafe { let mut native: libc::pthread_t = mem::zeroed(); - let attr: libc::pthread_attr_t = mem::zeroed(); - // assert_eq!(libc::pthread_attr_init(&mut attr), 0); FIXME: this function is not yet implemented. let thread_start: extern "C" fn() -> *mut libc::c_void = thread_start; let thread_start: extern "C" fn(*mut libc::c_void) -> *mut libc::c_void = mem::transmute(thread_start); - assert_eq!(libc::pthread_create(&mut native, &attr, thread_start, ptr::null_mut()), 0); + assert_eq!( + libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + 0 + ); assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs index 0af3600854d..92d8a765e51 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs @@ -12,12 +12,13 @@ extern "C" fn thread_start(_null: *mut libc::c_void, _x: i32) -> *mut libc::c_vo fn main() { unsafe { let mut native: libc::pthread_t = mem::zeroed(); - let attr: libc::pthread_attr_t = mem::zeroed(); - // assert_eq!(libc::pthread_attr_init(&mut attr), 0); FIXME: this function is not yet implemented. let thread_start: extern "C" fn(*mut libc::c_void, i32) -> *mut libc::c_void = thread_start; let thread_start: extern "C" fn(*mut libc::c_void) -> *mut libc::c_void = mem::transmute(thread_start); - assert_eq!(libc::pthread_create(&mut native, &attr, thread_start, ptr::null_mut()), 0); + assert_eq!( + libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + 0 + ); assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs index 472d07f617e..1c6bd629635 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs @@ -11,9 +11,10 @@ extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { fn main() { unsafe { let mut native: libc::pthread_t = mem::zeroed(); - let attr: libc::pthread_attr_t = mem::zeroed(); - // assert_eq!(libc::pthread_attr_init(&mut attr), 0); FIXME: this function is not yet implemented. - assert_eq!(libc::pthread_create(&mut native, &attr, thread_start, ptr::null_mut()), 0); + assert_eq!( + libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + 0 + ); assert_eq!(libc::pthread_detach(native), 0); assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); //~ ERROR: Undefined Behavior: trying to join a detached thread } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs index 988c33868a6..b81214b217e 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs @@ -11,9 +11,10 @@ extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { fn main() { unsafe { let mut native: libc::pthread_t = mem::zeroed(); - let attr: libc::pthread_attr_t = mem::zeroed(); - // assert_eq!(libc::pthread_attr_init(&mut attr), 0); FIXME: this function is not yet implemented. - assert_eq!(libc::pthread_create(&mut native, &attr, thread_start, ptr::null_mut()), 0); + assert_eq!( + libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + 0 + ); assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); //~ ERROR: Undefined Behavior: trying to join an already joined thread } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs index b2a398e0a19..2f29731c6b3 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs @@ -14,9 +14,10 @@ extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { fn main() { unsafe { let mut native: libc::pthread_t = mem::zeroed(); - let attr: libc::pthread_attr_t = mem::zeroed(); - // assert_eq!(libc::pthread_attr_init(&mut attr), 0); FIXME: this function is not yet implemented. - assert_eq!(libc::pthread_create(&mut native, &attr, thread_start, ptr::null_mut()), 0); + assert_eq!( + libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + 0 + ); let mut native_copy: libc::pthread_t = mem::zeroed(); ptr::copy_nonoverlapping(&native, &mut native_copy, 1); let handle = thread::spawn(move || { diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.init.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.init.stderr index 15f397d4ac2..7df8e8be580 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.init.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.init.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: pthread_mutex_t can't be moved after first use +error: Undefined Behavior: `pthread_mutex_t` can't be moved after first use --> tests/fail-dep/concurrency/libc_pthread_mutex_move.rs:LL:CC | LL | libc::pthread_mutex_lock(&mut m2 as *mut _); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `pthread_mutex_t` can't be moved after first use | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.rs index c12a97a9ca1..6c1f967b2b0 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.rs @@ -12,7 +12,7 @@ fn check() { assert_eq!(libc::pthread_mutex_init(&mut m as *mut _, std::ptr::null()), 0); let mut m2 = m; // move the mutex - libc::pthread_mutex_lock(&mut m2 as *mut _); //~[init] ERROR: pthread_mutex_t can't be moved after first use + libc::pthread_mutex_lock(&mut m2 as *mut _); //~[init] ERROR: can't be moved after first use } } @@ -23,6 +23,6 @@ fn check() { libc::pthread_mutex_lock(&mut m as *mut _); let mut m2 = m; // move the mutex - libc::pthread_mutex_unlock(&mut m2 as *mut _); //~[static_initializer] ERROR: pthread_mutex_t can't be moved after first use + libc::pthread_mutex_unlock(&mut m2 as *mut _); //~[static_initializer] ERROR: can't be moved after first use } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.static_initializer.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.static_initializer.stderr index ebc253bf7a6..acc018cb4ba 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.static_initializer.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.static_initializer.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: pthread_mutex_t can't be moved after first use +error: Undefined Behavior: `pthread_mutex_t` can't be moved after first use --> tests/fail-dep/concurrency/libc_pthread_mutex_move.rs:LL:CC | LL | libc::pthread_mutex_unlock(&mut m2 as *mut _); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `pthread_mutex_t` can't be moved after first use | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail-dep/concurrency/libx_pthread_rwlock_moved.rs b/src/tools/miri/tests/fail-dep/concurrency/libx_pthread_rwlock_moved.rs index 540729962a9..6af19b7df9b 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libx_pthread_rwlock_moved.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libx_pthread_rwlock_moved.rs @@ -9,6 +9,6 @@ fn main() { // Move rwlock let mut rw2 = rw; - libc::pthread_rwlock_unlock(&mut rw2 as *mut _); //~ ERROR: pthread_rwlock_t can't be moved after first use + libc::pthread_rwlock_unlock(&mut rw2 as *mut _); //~ ERROR: can't be moved after first use } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libx_pthread_rwlock_moved.stderr b/src/tools/miri/tests/fail-dep/concurrency/libx_pthread_rwlock_moved.stderr index ce08fa8159c..fbc9119f110 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libx_pthread_rwlock_moved.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libx_pthread_rwlock_moved.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: pthread_rwlock_t can't be moved after first use +error: Undefined Behavior: `pthread_rwlock_t` can't be moved after first use --> tests/fail-dep/concurrency/libx_pthread_rwlock_moved.rs:LL:CC | LL | libc::pthread_rwlock_unlock(&mut rw2 as *mut _); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_rwlock_t can't be moved after first use + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `pthread_rwlock_t` can't be moved after first use | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs index b763121080e..4b6f344a78e 100644 --- a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs @@ -8,5 +8,5 @@ fn main() { fn test_file_open_missing_needed_mode() { let name = b"missing_arg.txt\0"; let name_ptr = name.as_ptr().cast::<libc::c_char>(); - let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; //~ ERROR: Undefined Behavior: incorrect number of arguments for `open` with `O_CREAT`: got 2, expected at least 3 + let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; //~ ERROR: Undefined Behavior: incorrect number of arguments for `open(pathname, O_CREAT, ...)`: got 2, expected at least 3 } diff --git a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr index 971a2d76053..ca9e3c6c4be 100644 --- a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr +++ b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: incorrect number of arguments for `open` with `O_CREAT`: got 2, expected at least 3 +error: Undefined Behavior: incorrect number of arguments for `open(pathname, O_CREAT, ...)`: got 2, expected at least 3 --> tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs:LL:CC | -LL | ...safe { libc::open(name_ptr, libc::O_CREAT) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of arguments for `open` with `O_CREAT`: got 2, expected at least 3 +LL | ... { libc::open(name_ptr, libc::O_CREAT) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of arguments for `open(pathname, O_CREAT, ...)`: got 2, expected at least 3 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs new file mode 100644 index 00000000000..398bc92b392 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs @@ -0,0 +1,97 @@ +//! This ensures that when an epoll_wait wakes up and there are multiple events, +//! and we only read one of them, we do not synchronize with the other events +//! and therefore still report a data race for things that need to see the second event +//! to be considered synchronized. +//@only-target: linux +// ensure deterministic schedule +//@compile-flags: -Zmiri-preemption-rate=0 + +use std::convert::TryInto; +use std::thread; +use std::thread::spawn; + +#[track_caller] +fn check_epoll_wait<const N: usize>(epfd: i32, expected_notifications: &[(u32, u64)]) { + let epoll_event = libc::epoll_event { events: 0, u64: 0 }; + let mut array: [libc::epoll_event; N] = [epoll_event; N]; + let maxsize = N; + let array_ptr = array.as_mut_ptr(); + let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), 0) }; + if res < 0 { + panic!("epoll_wait failed: {}", std::io::Error::last_os_error()); + } + assert_eq!( + res, + expected_notifications.len().try_into().unwrap(), + "got wrong number of notifications" + ); + let slice = unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) }; + for (return_event, expected_event) in slice.iter().zip(expected_notifications.iter()) { + let event = return_event.events; + let data = return_event.u64; + assert_eq!(event, expected_event.0, "got wrong events"); + assert_eq!(data, expected_event.1, "got wrong data"); + } +} + +fn main() { + // Create an epoll instance. + let epfd = unsafe { libc::epoll_create1(0) }; + assert_ne!(epfd, -1); + + // Create two socketpair instances. + let mut fds_a = [-1, -1]; + let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds_a.as_mut_ptr()) }; + assert_eq!(res, 0); + + let mut fds_b = [-1, -1]; + let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds_b.as_mut_ptr()) }; + assert_eq!(res, 0); + + // Register both pipe read ends. + let mut ev = libc::epoll_event { + events: (libc::EPOLLIN | libc::EPOLLET) as _, + u64: u64::try_from(fds_a[1]).unwrap(), + }; + let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds_a[1], &mut ev) }; + assert_eq!(res, 0); + + let mut ev = libc::epoll_event { + events: (libc::EPOLLIN | libc::EPOLLET) as _, + u64: u64::try_from(fds_b[1]).unwrap(), + }; + let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds_b[1], &mut ev) }; + assert_eq!(res, 0); + + static mut VAL_ONE: u8 = 40; // This one will be read soundly. + static mut VAL_TWO: u8 = 50; // This one will be read unsoundly. + let thread1 = spawn(move || { + unsafe { VAL_ONE = 41 }; + + let data = "abcde".as_bytes().as_ptr(); + let res = unsafe { libc::write(fds_a[0], data as *const libc::c_void, 5) }; + assert_eq!(res, 5); + + unsafe { VAL_TWO = 51 }; + + let res = unsafe { libc::write(fds_b[0], data as *const libc::c_void, 5) }; + assert_eq!(res, 5); + }); + thread::yield_now(); + + // With room for one event: check result from epoll_wait. + let expected_event = u32::try_from(libc::EPOLLIN).unwrap(); + let expected_value = u64::try_from(fds_a[1]).unwrap(); + check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)]); + + // Since we only received one event, we have synchronized with + // the write to VAL_ONE but not with the one to VAL_TWO. + unsafe { + assert_eq!({ VAL_ONE }, 41) // This one is not UB + }; + unsafe { + assert_eq!({ VAL_TWO }, 51) //~ERROR: Data race detected + }; + + thread1.join().unwrap(); +} diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.stderr b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.stderr new file mode 100644 index 00000000000..a16c86f90ed --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic read on thread `main` at ALLOC. (2) just happened here + --> tests/fail-dep/libc/libc-epoll-data-race.rs:LL:CC + | +LL | assert_eq!({ VAL_TWO }, 51) + | ^^^^^^^ Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic read on thread `main` at ALLOC. (2) just happened here + | +help: and (1) occurred earlier here + --> tests/fail-dep/libc/libc-epoll-data-race.rs:LL:CC + | +LL | unsafe { VAL_TWO = 51 }; + | ^^^^^^^^^^^^ + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE (of the first span): + = note: inside `main` at tests/fail-dep/libc/libc-epoll-data-race.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs new file mode 100644 index 00000000000..f4c009456d2 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs @@ -0,0 +1,31 @@ +//! This is a regression test for <https://github.com/rust-lang/miri/issues/3947>: we had some +//! faulty logic around `release_clock` that led to this code not reporting a data race. +//@ignore-target: windows # no libc socketpair on Windows +//@compile-flags: -Zmiri-preemption-rate=0 +use std::thread; + +fn main() { + static mut VAL: u8 = 0; + let mut fds = [-1, -1]; + let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + assert_eq!(res, 0); + let thread1 = thread::spawn(move || { + let data = "a".as_bytes().as_ptr(); + let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 1) }; + assert_eq!(res, 1); + // The write to VAL is *after* the write to the socket, so there's no proper synchronization. + unsafe { VAL = 1 }; + }); + thread::yield_now(); + + let mut buf: [u8; 1] = [0; 1]; + let res: i32 = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 1); + assert_eq!(buf, "a".as_bytes()); + + unsafe { assert_eq!({ VAL }, 1) }; //~ERROR: Data race + + thread1.join().unwrap(); +} diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.stderr new file mode 100644 index 00000000000..6472c33727c --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic read on thread `main` at ALLOC. (2) just happened here + --> tests/fail-dep/libc/socketpair-data-race.rs:LL:CC + | +LL | unsafe { assert_eq!({ VAL }, 1) }; + | ^^^ Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic read on thread `main` at ALLOC. (2) just happened here + | +help: and (1) occurred earlier here + --> tests/fail-dep/libc/socketpair-data-race.rs:LL:CC + | +LL | unsafe { VAL = 1 }; + | ^^^^^^^ + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE (of the first span): + = note: inside `main` at tests/fail-dep/libc/socketpair-data-race.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr index dff332f9992..16892614c63 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: socketpair read: blocking isn't supported yet +error: unsupported operation: socketpair/pipe/pipe2 read: blocking isn't supported yet --> tests/fail-dep/libc/socketpair_read_blocking.rs:LL:CC | LL | let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair read: blocking isn't supported yet + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair/pipe/pipe2 read: blocking isn't supported yet | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr index 0dd89a15c7c..a2fcf87578a 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: socketpair write: blocking isn't supported yet +error: unsupported operation: socketpair/pipe/pipe2 write: blocking isn't supported yet --> tests/fail-dep/libc/socketpair_write_blocking.rs:LL:CC | LL | let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair write: blocking isn't supported yet + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair/pipe/pipe2 write: blocking isn't supported yet | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs new file mode 100644 index 00000000000..b996fcaf45d --- /dev/null +++ b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs @@ -0,0 +1,16 @@ +//@error-in-other-file: deadlock +//@normalize-stderr-test: "src/sys/.*\.rs" -> "$$FILE" +//@normalize-stderr-test: "LL \| .*" -> "LL | $$CODE" +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "\n *= note:.*" -> "" +use std::mem; +use std::sync::Mutex; + +fn main() { + let m = Mutex::new(0); + mem::forget(m.lock()); + // Move the lock while it is "held" (really: leaked) + let m2 = m; + // Now try to acquire the lock again. + let _guard = m2.lock(); +} diff --git a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr new file mode 100644 index 00000000000..0ca8b3558d4 --- /dev/null +++ b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr @@ -0,0 +1,16 @@ +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/$FILE:LL:CC + | +LL | $CODE + | ^ the evaluated program deadlocked + | +note: inside `main` + --> tests/fail/concurrency/mutex-leak-move-deadlock.rs:LL:CC + | +LL | $CODE + | ^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs index 3f8c9b6219a..a47bb671e32 100644 --- a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs +++ b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs @@ -16,12 +16,15 @@ impl Foo { pub fn main() { let mut f = Foo(0); - let inner = &mut f.0 as *mut u64; - let _res = f.add(unsafe { - let n = f.0; + let alias = &mut f.0 as *mut u64; + let res = f.add(unsafe { // This is the access at fault, but it's not immediately apparent because // the reference that got invalidated is not under a Protector. - *inner = 42; - n + *alias = 42; + 0 }); + // `res` could be optimized to be `0`, since at the time the reference for the `self` argument + // is created, it has value `0`, and then later we add `0` to that. But turns out there is + // a sneaky alias that's used to change the value of `*self` before it is read... + assert_eq!(res, 42); } diff --git a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.stderr b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.stderr index b85aac7db76..21178dad050 100644 --- a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.stderr @@ -9,12 +9,12 @@ LL | fn add(&mut self, n: u64) -> u64 { help: the accessed tag <TAG> was created here, in the initial state Reserved --> tests/fail/tree_borrows/write-during-2phase.rs:LL:CC | -LL | let _res = f.add(unsafe { - | ^ +LL | let res = f.add(unsafe { + | ^ help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] --> tests/fail/tree_borrows/write-during-2phase.rs:LL:CC | -LL | *inner = 42; +LL | *alias = 42; | ^^^^^^^^^^^ = help: this transition corresponds to a loss of read and write permissions = note: BACKTRACE (of the first span): @@ -22,13 +22,12 @@ LL | *inner = 42; note: inside `main` --> tests/fail/tree_borrows/write-during-2phase.rs:LL:CC | -LL | let _res = f.add(unsafe { - | ________________^ -LL | | let n = f.0; +LL | let res = f.add(unsafe { + | _______________^ LL | | // This is the access at fault, but it's not immediately apparent because LL | | // the reference that got invalidated is not under a Protector. -LL | | *inner = 42; -LL | | n +LL | | *alias = 42; +LL | | 0 LL | | }); | |______^ diff --git a/src/tools/miri/tests/panic/unsupported_foreign_function.rs b/src/tools/miri/tests/panic/unsupported_foreign_function.rs deleted file mode 100644 index b8301c50772..00000000000 --- a/src/tools/miri/tests/panic/unsupported_foreign_function.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@compile-flags: -Zmiri-panic-on-unsupported -//@normalize-stderr-test: "OS `.*`" -> "$$OS" - -fn main() { - extern "Rust" { - fn foo(); - } - - unsafe { - foo(); - } -} diff --git a/src/tools/miri/tests/panic/unsupported_foreign_function.stderr b/src/tools/miri/tests/panic/unsupported_foreign_function.stderr deleted file mode 100644 index 278af9612d6..00000000000 --- a/src/tools/miri/tests/panic/unsupported_foreign_function.stderr +++ /dev/null @@ -1,4 +0,0 @@ -thread 'main' panicked at tests/panic/unsupported_foreign_function.rs:LL:CC: -unsupported Miri functionality: can't call foreign function `foo` on $OS -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/src/tools/miri/tests/panic/unsupported_syscall.rs b/src/tools/miri/tests/panic/unsupported_syscall.rs deleted file mode 100644 index bbb076b169a..00000000000 --- a/src/tools/miri/tests/panic/unsupported_syscall.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ignore-target: windows # no `syscall` on Windows -//@ignore-target: apple # `syscall` is not supported on macOS -//@compile-flags: -Zmiri-panic-on-unsupported - -fn main() { - unsafe { - libc::syscall(0); - } -} diff --git a/src/tools/miri/tests/panic/unsupported_syscall.stderr b/src/tools/miri/tests/panic/unsupported_syscall.stderr deleted file mode 100644 index e9b2b5b6652..00000000000 --- a/src/tools/miri/tests/panic/unsupported_syscall.stderr +++ /dev/null @@ -1,4 +0,0 @@ -thread 'main' panicked at tests/panic/unsupported_syscall.rs:LL:CC: -unsupported Miri functionality: can't execute syscall with ID 0 -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/src/tools/miri/tests/pass-dep/concurrency/apple-os-unfair-lock.rs b/src/tools/miri/tests/pass-dep/concurrency/apple-os-unfair-lock.rs index 0fc432f24c8..f5b64474f83 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/apple-os-unfair-lock.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/apple-os-unfair-lock.rs @@ -16,8 +16,8 @@ fn main() { // `os_unfair_lock`s can be moved and leaked. // In the real implementation, even moving it while locked is possible - // (and "forks" the lock, i.e. old and new location have independent wait queues); - // Miri behavior differs here and anyway none of this is documented. + // (and "forks" the lock, i.e. old and new location have independent wait queues). + // We only test the somewhat sane case of moving while unlocked that `std` plans to rely on. let lock = lock; let locked = unsafe { libc::os_unfair_lock_trylock(lock.get()) }; assert!(locked); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index d7675a40163..9bcc776e281 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs @@ -161,7 +161,7 @@ fn test_epoll_race() { // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; - // read returns number of bytes that have been read, which is always 8. + // write returns number of bytes written, which is always 8. assert_eq!(res, 8); }); thread::yield_now(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index 1d084194658..c92d9c3fe70 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -10,6 +10,7 @@ use std::thread; fn main() { test_read_write(); test_race(); + test_syscall(); } fn read_bytes<const N: usize>(fd: i32, buf: &mut [u8; N]) -> i32 { @@ -109,3 +110,11 @@ fn test_race() { thread::yield_now(); thread1.join().unwrap(); } + +// This is a test for calling eventfd2 through a syscall. +fn test_syscall() { + let initval = 0 as libc::c_uint; + let flags = (libc::EFD_CLOEXEC | libc::EFD_NONBLOCK) as libc::c_int; + let fd = unsafe { libc::syscall(libc::SYS_eventfd2, initval, flags) }; + assert_ne!(fd, -1); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index 76f883e5d8d..01433edf9a3 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -7,6 +7,14 @@ fn main() { test_pipe_threaded(); test_race(); test_pipe_array(); + #[cfg(any( + target_os = "linux", + target_os = "illumos", + target_os = "freebsd", + target_os = "solaris" + ))] + // `pipe2` only exists in some specific os. + test_pipe2(); } fn test_pipe() { @@ -110,3 +118,16 @@ fn test_pipe_array() { let mut fds: [i32; 2] = [0; 2]; assert_eq!(unsafe { pipe(&mut fds) }, 0); } + +/// Test if pipe2 (including the O_NONBLOCK flag) is supported. +#[cfg(any( + target_os = "linux", + target_os = "illumos", + target_os = "freebsd", + target_os = "solaris" +))] +fn test_pipe2() { + let mut fds = [-1, -1]; + let res = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_NONBLOCK) }; + assert_eq!(res, 0); +} diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index a94f960f442..404ef7cc42d 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -1,10 +1,27 @@ //@ignore-target: windows # No pthreads on Windows -use std::ffi::CStr; -#[cfg(not(target_os = "freebsd"))] -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::thread; +const MAX_THREAD_NAME_LEN: usize = { + cfg_if::cfg_if! { + if #[cfg(any(target_os = "linux"))] { + 16 + } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + 32 + } else if #[cfg(target_os = "macos")] { + libc::MAXTHREADNAMESIZE // 64, at the time of writing + } else if #[cfg(target_os = "freebsd")] { + usize::MAX // as far as I can tell + } else { + panic!() + } + } +}; + fn main() { + // The short name should be shorter than 16 bytes which POSIX promises + // for thread names. The length includes a null terminator. + let short_name = "test_named".to_owned(); let long_name = std::iter::once("test_named_thread_truncation") .chain(std::iter::repeat(" yada").take(100)) .collect::<String>(); @@ -48,23 +65,120 @@ fn main() { } } - let result = thread::Builder::new().name(long_name.clone()).spawn(move || { - // Rust remembers the full thread name itself. - assert_eq!(thread::current().name(), Some(long_name.as_str())); + thread::Builder::new() + .spawn(move || { + // Set short thread name. + let cstr = CString::new(short_name.clone()).unwrap(); + assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); // this should fit + assert_eq!(set_thread_name(&cstr), 0); + + // Now get it again, in various ways. + + // POSIX seems to promise at least 15 chars excluding a null terminator. + let mut buf = vec![0u8; short_name.len().max(15) + 1]; + assert_eq!(get_thread_name(&mut buf), 0); + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert_eq!(cstr.to_bytes(), short_name.as_bytes()); + + // Test what happens when the buffer is shorter than 16, but still long enough. + let res = get_thread_name(&mut buf[..15]); + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + // For glibc used by linux-gnu there should be a failue, + // if a shorter than 16 bytes buffer is provided, even if that would be + // large enough for the thread name. + assert_eq!(res, libc::ERANGE); + } else { + // Everywhere else, this should work. + assert_eq!(res, 0); + // POSIX seems to promise at least 15 chars excluding a null terminator. + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert_eq!(short_name.as_bytes(), cstr.to_bytes()); + } + } + + // Test what happens when the buffer is too short even for the short name. + let res = get_thread_name(&mut buf[..4]); + cfg_if::cfg_if! { + if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + // On macOS and FreeBSD it's not an error for the buffer to be + // too short for the thread name -- they truncate instead. + assert_eq!(res, 0); + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert_eq!(cstr.to_bytes_with_nul().len(), 4); + assert!(short_name.as_bytes().starts_with(cstr.to_bytes())); + } else { + // The rest should give an error. + assert_eq!(res, libc::ERANGE); + } + } + + // Test zero-sized buffer. + let res = get_thread_name(&mut []); + cfg_if::cfg_if! { + if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + // On macOS and FreeBSD it's not an error for the buffer to be + // too short for the thread name -- even with size 0. + assert_eq!(res, 0); + } else { + // The rest should give an error. + assert_eq!(res, libc::ERANGE); + } + } + }) + .unwrap() + .join() + .unwrap(); + + thread::Builder::new() + .spawn(move || { + // Set full thread name. + let cstr = CString::new(long_name.clone()).unwrap(); + let res = set_thread_name(&cstr); + cfg_if::cfg_if! { + if #[cfg(target_os = "freebsd")] { + // Names of all size are supported. + assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); + assert_eq!(res, 0); + } else if #[cfg(target_os = "macos")] { + // Name is too long. + assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); + assert_eq!(res, libc::ENAMETOOLONG); + } else { + // Name is too long. + assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); + assert_eq!(res, libc::ERANGE); + } + } + // Set the longest name we can. + let truncated_name = &long_name[..long_name.len().min(MAX_THREAD_NAME_LEN - 1)]; + let cstr = CString::new(truncated_name).unwrap(); + assert_eq!(set_thread_name(&cstr), 0); + + // Now get it again, in various ways. - // But the system is limited -- make sure we successfully set a truncation. - let mut buf = vec![0u8; long_name.len() + 1]; - assert_eq!(get_thread_name(&mut buf), 0); - let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); - assert!(cstr.to_bytes().len() >= 15, "name is too short: len={}", cstr.to_bytes().len()); // POSIX seems to promise at least 15 chars - assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); + // This name should round-trip properly. + let mut buf = vec![0u8; long_name.len() + 1]; + assert_eq!(get_thread_name(&mut buf), 0); + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert_eq!(cstr.to_bytes(), truncated_name.as_bytes()); - // Also test directly calling pthread_setname to check its return value. - assert_eq!(set_thread_name(&cstr), 0); - // But with a too long name it should fail (except on FreeBSD where the - // function has no return, hence cannot indicate failure). - #[cfg(not(target_os = "freebsd"))] - assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0); - }); - result.unwrap().join().unwrap(); + // Test what happens when our buffer is just one byte too small. + let res = get_thread_name(&mut buf[..truncated_name.len()]); + cfg_if::cfg_if! { + if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + // On macOS and FreeBSD it's not an error for the buffer to be + // too short for the thread name -- they truncate instead. + assert_eq!(res, 0); + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert_eq!(cstr.to_bytes(), &truncated_name.as_bytes()[..(truncated_name.len() - 1)]); + } else { + // The rest should give an error. + assert_eq!(res, libc::ERANGE); + } + } + }) + .unwrap() + .join() + .unwrap(); } diff --git a/src/tools/miri/tests/pass/concurrency/threadname.rs b/src/tools/miri/tests/pass/concurrency/threadname.rs index 6dd5f1f5c91..41cac6459b2 100644 --- a/src/tools/miri/tests/pass/concurrency/threadname.rs +++ b/src/tools/miri/tests/pass/concurrency/threadname.rs @@ -16,6 +16,19 @@ fn main() { .join() .unwrap(); + // Long thread name. + let long_name = std::iter::once("test_named_thread_truncation") + .chain(std::iter::repeat(" long").take(100)) + .collect::<String>(); + thread::Builder::new() + .name(long_name.clone()) + .spawn(move || { + assert_eq!(thread::current().name().unwrap(), long_name); + }) + .unwrap() + .join() + .unwrap(); + // Also check main thread name. assert_eq!(thread::current().name().unwrap(), "main"); } diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py index 860d21876de..328b48e04d2 100755 --- a/src/tools/publish_toolstate.py +++ b/src/tools/publish_toolstate.py @@ -235,7 +235,9 @@ try: exit(0) cur_commit = sys.argv[1] - cur_datetime = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') + cur_datetime = datetime.datetime.now(datetime.timezone.utc).strftime( + '%Y-%m-%dT%H:%M:%SZ' + ) cur_commit_msg = sys.argv[2] save_message_to_path = sys.argv[3] github_token = sys.argv[4] diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs index 555a9240798..4d684f3c635 100644 --- a/src/tools/rustfmt/src/spanned.rs +++ b/src/tools/rustfmt/src/spanned.rs @@ -180,7 +180,7 @@ impl Spanned for ast::GenericArg { impl Spanned for ast::GenericBound { fn span(&self) -> Span { match *self { - ast::GenericBound::Trait(ref ptr, _) => ptr.span, + ast::GenericBound::Trait(ref ptr) => ptr.span, ast::GenericBound::Outlives(ref l) => l.ident.span, ast::GenericBound::Use(_, span) => span, } diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index f75c4f0fad7..99b3fe60ee2 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -610,16 +610,14 @@ impl Rewrite for ast::GenericBound { fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - ast::GenericBound::Trait( - ref poly_trait_ref, - ast::TraitBoundModifiers { + ast::GenericBound::Trait(ref poly_trait_ref) => { + let snippet = context.snippet(self.span()); + let has_paren = snippet.starts_with('(') && snippet.ends_with(')'); + let ast::TraitBoundModifiers { constness, asyncness, polarity, - }, - ) => { - let snippet = context.snippet(self.span()); - let has_paren = snippet.starts_with('(') && snippet.ends_with(')'); + } = poly_trait_ref.modifiers; let mut constness = constness.as_str().to_string(); if !constness.is_empty() { constness.push(' '); @@ -827,7 +825,8 @@ impl Rewrite for ast::Ty { rewrite_unary_prefix(context, prefix, &*mt.ty, shape) } - ast::TyKind::Ref(ref lifetime, ref mt) => { + ast::TyKind::Ref(ref lifetime, ref mt) + | ast::TyKind::PinnedRef(ref lifetime, ref mt) => { let mut_str = format_mutability(mt.mutbl); let mut_len = mut_str.len(); let mut result = String::with_capacity(128); @@ -861,6 +860,13 @@ impl Rewrite for ast::Ty { cmnt_lo = lifetime.ident.span.hi(); } + if let ast::TyKind::PinnedRef(..) = self.kind { + result.push_str("pin "); + if ast::Mutability::Not == mt.mutbl { + result.push_str("const "); + } + } + if ast::Mutability::Mut == mt.mutbl { let mut_hi = context.snippet_provider.span_after(self.span(), "mut"); let before_mut_span = mk_sp(cmnt_lo, mut_hi - BytePos::from_usize(3)); @@ -1260,9 +1266,9 @@ pub(crate) fn can_be_overflowed_type( ) -> bool { match ty.kind { ast::TyKind::Tup(..) => context.use_block_indent() && len == 1, - ast::TyKind::Ref(_, ref mutty) | ast::TyKind::Ptr(ref mutty) => { - can_be_overflowed_type(context, &*mutty.ty, len) - } + ast::TyKind::Ref(_, ref mutty) + | ast::TyKind::PinnedRef(_, ref mutty) + | ast::TyKind::Ptr(ref mutty) => can_be_overflowed_type(context, &*mutty.ty, len), _ => false, } } diff --git a/src/tools/rustfmt/tests/source/pin_sugar.rs b/src/tools/rustfmt/tests/source/pin_sugar.rs new file mode 100644 index 00000000000..0eb3c0770c4 --- /dev/null +++ b/src/tools/rustfmt/tests/source/pin_sugar.rs @@ -0,0 +1,10 @@ +// See #130494 + +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +fn f(x: &pin const i32) {} +fn g<'a>(x: & 'a pin const i32) {} +fn h<'a>(x: & 'a pin +mut i32) {} +fn i(x: &pin mut i32) {} diff --git a/src/tools/rustfmt/tests/target/pin_sugar.rs b/src/tools/rustfmt/tests/target/pin_sugar.rs new file mode 100644 index 00000000000..c9fa883e238 --- /dev/null +++ b/src/tools/rustfmt/tests/target/pin_sugar.rs @@ -0,0 +1,9 @@ +// See #130494 + +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +fn f(x: &pin const i32) {} +fn g<'a>(x: &'a pin const i32) {} +fn h<'a>(x: &'a pin mut i32) {} +fn i(x: &pin mut i32) {} diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index bfffa1eee60..8097d6a8caf 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -77,7 +77,7 @@ impl RawEmitter { writeln!( &mut self.file, - "const BITSET_CANONICAL: &'static [u64; {}] = &[{}];", + "static BITSET_CANONICAL: [u64; {}] = [{}];", canonicalized.canonical_words.len(), fmt_list(canonicalized.canonical_words.iter().map(|v| Bits(*v))), ) @@ -85,7 +85,7 @@ impl RawEmitter { self.bytes_used += 8 * canonicalized.canonical_words.len(); writeln!( &mut self.file, - "const BITSET_MAPPING: &'static [(u8, u8); {}] = &[{}];", + "static BITSET_MAPPING: [(u8, u8); {}] = [{}];", canonicalized.canonicalized_words.len(), fmt_list(&canonicalized.canonicalized_words), ) @@ -139,7 +139,7 @@ impl RawEmitter { writeln!( &mut self.file, - "const BITSET_CHUNKS_MAP: &'static [u8; {}] = &[{}];", + "static BITSET_CHUNKS_MAP: [u8; {}] = [{}];", chunk_indices.len(), fmt_list(&chunk_indices), ) @@ -147,7 +147,7 @@ impl RawEmitter { self.bytes_used += chunk_indices.len(); writeln!( &mut self.file, - "const BITSET_INDEX_CHUNKS: &'static [[u8; {}]; {}] = &[{}];", + "static BITSET_INDEX_CHUNKS: [[u8; {}]; {}] = [{}];", chunk_length, chunks.len(), fmt_list(chunks.iter()), diff --git a/src/version b/src/version index 6b4de0a42b0..bd0f9e6c28f 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.83.0 +1.84.0 diff --git a/tests/codegen/asm-arm64ec-clobbers.rs b/tests/codegen/asm-arm64ec-clobbers.rs new file mode 100644 index 00000000000..2ec61907947 --- /dev/null +++ b/tests/codegen/asm-arm64ec-clobbers.rs @@ -0,0 +1,36 @@ +//@ assembly-output: emit-asm +//@ compile-flags: --target arm64ec-pc-windows-msvc +//@ needs-llvm-components: aarch64 + +#![crate_type = "rlib"] +#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} + +// CHECK-LABEL: @cc_clobber +// CHECK: call void asm sideeffect "", "~{cc}"() +#[no_mangle] +pub unsafe fn cc_clobber() { + asm!("", options(nostack, nomem)); +} + +// CHECK-LABEL: @no_clobber +// CHECK: call void asm sideeffect "", ""() +#[no_mangle] +pub unsafe fn no_clobber() { + asm!("", options(nostack, nomem, preserves_flags)); +} + +// CHECK-LABEL: @clobber_abi +// CHECK: asm sideeffect "", "={w0},={w1},={w2},={w3},={w4},={w5},={w6},={w7},={w8},={w9},={w10},={w11},={w12},={w15},={w16},={w17},={w30},={q0},={q1},={q2},={q3},={q4},={q5},={q6},={q7},={q8},={q9},={q10},={q11},={q12},={q13},={q14},={q15}"() +#[no_mangle] +pub unsafe fn clobber_abi() { + asm!("", clobber_abi("C"), options(nostack, nomem, preserves_flags)); +} diff --git a/tests/crashes/120241-2.rs b/tests/crashes/120241-2.rs deleted file mode 100644 index 91ec3362090..00000000000 --- a/tests/crashes/120241-2.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ known-bug: #120241 -//@ edition:2021 -#![feature(dyn_compatible_for_dispatch)] -#![feature(unsized_fn_params)] - -fn guard(_s: Copy) -> bool { - panic!() -} - -fn main() {} diff --git a/tests/crashes/120241.rs b/tests/crashes/120241.rs deleted file mode 100644 index b4fcb903714..00000000000 --- a/tests/crashes/120241.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #120241 -//@ edition:2021 -#![feature(dyn_compatible_for_dispatch)] - -trait B { - fn f(a: A) -> A; -} - -trait A { - fn g(b: B) -> B; -} - -fn main() {} diff --git a/tests/crashes/120482.rs b/tests/crashes/120482.rs deleted file mode 100644 index a395855d796..00000000000 --- a/tests/crashes/120482.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #120482 -//@ edition:2021 -#![feature(dyn_compatible_for_dispatch)] - -trait B { - fn bar(&self, x: &Self); -} - -trait A { - fn g(new: B) -> B; -} - -fn main() {} diff --git a/tests/crashes/125512.rs b/tests/crashes/125512.rs deleted file mode 100644 index 37dbdf2f32f..00000000000 --- a/tests/crashes/125512.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ known-bug: rust-lang/rust#125512 -//@ edition:2021 -#![feature(dyn_compatible_for_dispatch)] -trait B { - fn f(a: A) -> A; -} -trait A { - fn concrete(b: B) -> B; -} -fn main() {} diff --git a/tests/crashes/131507.rs b/tests/crashes/131507.rs new file mode 100644 index 00000000000..d402fb8afc3 --- /dev/null +++ b/tests/crashes/131507.rs @@ -0,0 +1,10 @@ +//@ known-bug: #131507 +//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir +#![feature(non_lifetime_binders)] + +fn brick() +where + for<T> T: Copy, +{ + || format_args!(""); +} diff --git a/tests/crashes/131534.rs b/tests/crashes/131534.rs new file mode 100644 index 00000000000..545b3e68fe8 --- /dev/null +++ b/tests/crashes/131534.rs @@ -0,0 +1,5 @@ +//@ known-bug: #131534 +#![feature(generic_const_exprs)] +type Value<'v> = &[[u8; SIZE]]; + +trait Trait: Fn(Value) -> Value {} diff --git a/tests/crashes/131535.rs b/tests/crashes/131535.rs new file mode 100644 index 00000000000..47ccdf87f2d --- /dev/null +++ b/tests/crashes/131535.rs @@ -0,0 +1,4 @@ +//@ known-bug: #131535 +#![feature(non_lifetime_binders)] +trait v0<> {} +fn kind :(v0<'_, > impl for<v4> v0<'_, v2 = impl v0<v4> + '_>) {} diff --git a/tests/crashes/131538.rs b/tests/crashes/131538.rs new file mode 100644 index 00000000000..f971d8b7791 --- /dev/null +++ b/tests/crashes/131538.rs @@ -0,0 +1,13 @@ +//@ known-bug: #131538 +#![feature(generic_associated_types_extended)] +#![feature(trivial_bounds)] + +trait HealthCheck { + async fn check<const N: usize>(); +} + +fn do_health_check_par() +where + HealthCheck: HealthCheck, +{ +} diff --git a/tests/debuginfo/type-names.rs b/tests/debuginfo/type-names.rs index 6831786c228..4caaf3fc97f 100644 --- a/tests/debuginfo/type-names.rs +++ b/tests/debuginfo/type-names.rs @@ -17,7 +17,7 @@ // gdb-check:type = type_names::GenericStruct<type_names::mod1::Struct2, type_names::mod1::mod2::Struct3> // gdb-command:whatis generic_struct2 -// gdb-check:type = type_names::GenericStruct<type_names::Struct1, extern "fastcall" fn(isize) -> usize> +// gdb-check:type = type_names::GenericStruct<type_names::Struct1, extern "system" fn(isize) -> usize> // gdb-command:whatis mod_struct // gdb-check:type = type_names::mod1::Struct2 @@ -372,7 +372,7 @@ fn main() { let simple_struct = Struct1; let generic_struct1: GenericStruct<mod1::Struct2, mod1::mod2::Struct3> = GenericStruct(PhantomData); - let generic_struct2: GenericStruct<Struct1, extern "fastcall" fn(isize) -> usize> = + let generic_struct2: GenericStruct<Struct1, extern "system" fn(isize) -> usize> = GenericStruct(PhantomData); let mod_struct = mod1::Struct2; diff --git a/tests/pretty/autodiff_forward.pp b/tests/pretty/autodiff_forward.pp new file mode 100644 index 00000000000..23c3b5b34a8 --- /dev/null +++ b/tests/pretty/autodiff_forward.pp @@ -0,0 +1,107 @@ +#![feature(prelude_import)] +#![no_std] +//@ needs-enzyme + +#![feature(autodiff)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +//@ pretty-mode:expanded +//@ pretty-compare-only +//@ pp-exact:autodiff_forward.pp + +// Test that forward mode ad macros are expanded correctly. + +use std::autodiff::autodiff; + +#[rustc_autodiff] +#[inline(never)] +pub fn f1(x: &[f64], y: f64) -> f64 { + + + + // Not the most interesting derivative, but who are we to judge + + // We want to be sure that the same function can be differentiated in different ways + + ::core::panicking::panic("not implemented") +} +#[rustc_autodiff(Forward, Dual, Const, Dual,)] +#[inline(never)] +pub fn df1(x: &[f64], bx: &[f64], y: f64) -> (f64, f64) { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f1(x, y)); + ::core::hint::black_box((bx,)); + ::core::hint::black_box((f1(x, y), f64::default())) +} +#[rustc_autodiff] +#[inline(never)] +pub fn f2(x: &[f64], y: f64) -> f64 { + ::core::panicking::panic("not implemented") +} +#[rustc_autodiff(Forward, Dual, Const, Const,)] +#[inline(never)] +pub fn df2(x: &[f64], bx: &[f64], y: f64) -> f64 { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f2(x, y)); + ::core::hint::black_box((bx,)); + ::core::hint::black_box(f2(x, y)) +} +#[rustc_autodiff] +#[inline(never)] +pub fn f3(x: &[f64], y: f64) -> f64 { + ::core::panicking::panic("not implemented") +} +#[rustc_autodiff(ForwardFirst, Dual, Const, Const,)] +#[inline(never)] +pub fn df3(x: &[f64], bx: &[f64], y: f64) -> f64 { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f3(x, y)); + ::core::hint::black_box((bx,)); + ::core::hint::black_box(f3(x, y)) +} +#[rustc_autodiff] +#[inline(never)] +pub fn f4() {} +#[rustc_autodiff(Forward, None)] +#[inline(never)] +pub fn df4() { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f4()); + ::core::hint::black_box(()); +} +#[rustc_autodiff] +#[inline(never)] +#[rustc_autodiff] +#[inline(never)] +#[rustc_autodiff] +#[inline(never)] +pub fn f5(x: &[f64], y: f64) -> f64 { + ::core::panicking::panic("not implemented") +} +#[rustc_autodiff(Forward, Const, Dual, Const,)] +#[inline(never)] +pub fn df5_y(x: &[f64], y: f64, by: f64) -> f64 { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f5(x, y)); + ::core::hint::black_box((by,)); + ::core::hint::black_box(f5(x, y)) +} +#[rustc_autodiff(Forward, Dual, Const, Const,)] +#[inline(never)] +pub fn df5_x(x: &[f64], bx: &[f64], y: f64) -> f64 { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f5(x, y)); + ::core::hint::black_box((bx,)); + ::core::hint::black_box(f5(x, y)) +} +#[rustc_autodiff(Reverse, Duplicated, Const, Active,)] +#[inline(never)] +pub fn df5_rev(x: &[f64], dx: &mut [f64], y: f64, dret: f64) -> f64 { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f5(x, y)); + ::core::hint::black_box((dx, dret)); + ::core::hint::black_box(f5(x, y)) +} +fn main() {} diff --git a/tests/pretty/autodiff_forward.rs b/tests/pretty/autodiff_forward.rs new file mode 100644 index 00000000000..35108d0d6f1 --- /dev/null +++ b/tests/pretty/autodiff_forward.rs @@ -0,0 +1,39 @@ +//@ needs-enzyme + +#![feature(autodiff)] +//@ pretty-mode:expanded +//@ pretty-compare-only +//@ pp-exact:autodiff_forward.pp + +// Test that forward mode ad macros are expanded correctly. + +use std::autodiff::autodiff; + +#[autodiff(df1, Forward, Dual, Const, Dual)] +pub fn f1(x: &[f64], y: f64) -> f64 { + unimplemented!() +} + +#[autodiff(df2, Forward, Dual, Const, Const)] +pub fn f2(x: &[f64], y: f64) -> f64 { + unimplemented!() +} + +#[autodiff(df3, ForwardFirst, Dual, Const, Const)] +pub fn f3(x: &[f64], y: f64) -> f64 { + unimplemented!() +} + +// Not the most interesting derivative, but who are we to judge +#[autodiff(df4, Forward)] +pub fn f4() {} + +// We want to be sure that the same function can be differentiated in different ways +#[autodiff(df5_rev, Reverse, Duplicated, Const, Active)] +#[autodiff(df5_x, Forward, Dual, Const, Const)] +#[autodiff(df5_y, Forward, Const, Dual, Const)] +pub fn f5(x: &[f64], y: f64) -> f64 { + unimplemented!() +} + +fn main() {} diff --git a/tests/pretty/autodiff_reverse.pp b/tests/pretty/autodiff_reverse.pp new file mode 100644 index 00000000000..a98d3782c70 --- /dev/null +++ b/tests/pretty/autodiff_reverse.pp @@ -0,0 +1,86 @@ +#![feature(prelude_import)] +#![no_std] +//@ needs-enzyme + +#![feature(autodiff)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +//@ pretty-mode:expanded +//@ pretty-compare-only +//@ pp-exact:autodiff_reverse.pp + +// Test that reverse mode ad macros are expanded correctly. + +use std::autodiff::autodiff; + +#[rustc_autodiff] +#[inline(never)] +pub fn f1(x: &[f64], y: f64) -> f64 { + + // Not the most interesting derivative, but who are we to judge + + + // What happens if we already have Reverse in type (enum variant decl) and value (enum variant + // constructor) namespace? > It's expected to work normally. + + + ::core::panicking::panic("not implemented") +} +#[rustc_autodiff(Reverse, Duplicated, Const, Active,)] +#[inline(never)] +pub fn df1(x: &[f64], dx: &mut [f64], y: f64, dret: f64) -> f64 { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f1(x, y)); + ::core::hint::black_box((dx, dret)); + ::core::hint::black_box(f1(x, y)) +} +#[rustc_autodiff] +#[inline(never)] +pub fn f2() {} +#[rustc_autodiff(Reverse, None)] +#[inline(never)] +pub fn df2() { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f2()); + ::core::hint::black_box(()); +} +#[rustc_autodiff] +#[inline(never)] +pub fn f3(x: &[f64], y: f64) -> f64 { + ::core::panicking::panic("not implemented") +} +#[rustc_autodiff(ReverseFirst, Duplicated, Const, Active,)] +#[inline(never)] +pub fn df3(x: &[f64], dx: &mut [f64], y: f64, dret: f64) -> f64 { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f3(x, y)); + ::core::hint::black_box((dx, dret)); + ::core::hint::black_box(f3(x, y)) +} +enum Foo { Reverse, } +use Foo::Reverse; +#[rustc_autodiff] +#[inline(never)] +pub fn f4(x: f32) { ::core::panicking::panic("not implemented") } +#[rustc_autodiff(Reverse, Const, None)] +#[inline(never)] +pub fn df4(x: f32) { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f4(x)); + ::core::hint::black_box(()); +} +#[rustc_autodiff] +#[inline(never)] +pub fn f5(x: *const f32, y: &f32) { + ::core::panicking::panic("not implemented") +} +#[rustc_autodiff(Reverse, DuplicatedOnly, Duplicated, None)] +#[inline(never)] +pub unsafe fn df5(x: *const f32, dx: *mut f32, y: &f32, dy: &mut f32) { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f5(x, y)); + ::core::hint::black_box((dx, dy)); +} +fn main() {} diff --git a/tests/pretty/autodiff_reverse.rs b/tests/pretty/autodiff_reverse.rs new file mode 100644 index 00000000000..657201caa94 --- /dev/null +++ b/tests/pretty/autodiff_reverse.rs @@ -0,0 +1,40 @@ +//@ needs-enzyme + +#![feature(autodiff)] +//@ pretty-mode:expanded +//@ pretty-compare-only +//@ pp-exact:autodiff_reverse.pp + +// Test that reverse mode ad macros are expanded correctly. + +use std::autodiff::autodiff; + +#[autodiff(df1, Reverse, Duplicated, Const, Active)] +pub fn f1(x: &[f64], y: f64) -> f64 { + unimplemented!() +} + +// Not the most interesting derivative, but who are we to judge +#[autodiff(df2, Reverse)] +pub fn f2() {} + +#[autodiff(df3, ReverseFirst, Duplicated, Const, Active)] +pub fn f3(x: &[f64], y: f64) -> f64 { + unimplemented!() +} + +enum Foo { Reverse } +use Foo::Reverse; +// What happens if we already have Reverse in type (enum variant decl) and value (enum variant +// constructor) namespace? > It's expected to work normally. +#[autodiff(df4, Reverse, Const)] +pub fn f4(x: f32) { + unimplemented!() +} + +#[autodiff(df5, Reverse, DuplicatedOnly, Duplicated)] +pub fn f5(x: *const f32, y: &f32) { + unimplemented!() +} + +fn main() {} diff --git a/tests/run-make/README.md b/tests/run-make/README.md index 40359903473..9e1eaa881c9 100644 --- a/tests/run-make/README.md +++ b/tests/run-make/README.md @@ -29,7 +29,7 @@ The setup for the `rmake.rs` version is a 3-stage process: structure within `build/<target>/test/run-make/` ``` - <test-name>/ + <test-name>/<test-name>/ rmake.exe # recipe binary rmake_out/ # sources from test sources copied over ``` diff --git a/tests/run-make/non-unicode-env/non_unicode_env.rs b/tests/run-make/non-unicode-env/non_unicode_env.rs index 865fc937365..3efa4842d94 100644 --- a/tests/run-make/non-unicode-env/non_unicode_env.rs +++ b/tests/run-make/non-unicode-env/non_unicode_env.rs @@ -1,3 +1,4 @@ fn main() { let _ = env!("NON_UNICODE_VAR"); + let _ = option_env!("NON_UNICODE_VAR"); } diff --git a/tests/run-make/non-unicode-env/non_unicode_env.stderr b/tests/run-make/non-unicode-env/non_unicode_env.stderr index c4dcd7b2eb7..1f260ac9c07 100644 --- a/tests/run-make/non-unicode-env/non_unicode_env.stderr +++ b/tests/run-make/non-unicode-env/non_unicode_env.stderr @@ -6,5 +6,13 @@ error: environment variable `NON_UNICODE_VAR` is not a valid Unicode string | = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error +error: environment variable `NON_UNICODE_VAR` is not a valid Unicode string + --> non_unicode_env.rs:3:13 + | +3 | let _ = option_env!("NON_UNICODE_VAR"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `option_env` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors diff --git a/tests/rustdoc-json/fns/extern_safe.rs b/tests/rustdoc-json/fns/extern_safe.rs index a4a2d2c7f8c..b00f9f50bd2 100644 --- a/tests/rustdoc-json/fns/extern_safe.rs +++ b/tests/rustdoc-json/fns/extern_safe.rs @@ -2,7 +2,7 @@ extern "C" { //@ is "$.index[*][?(@.name=='f1')].inner.function.header.is_unsafe" true pub fn f1(); - // items in unadorned `extern` blocks cannot have safety qualifiers + // items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers } unsafe extern "C" { diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index e8cec632177..1577b68e748 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -178,7 +178,7 @@ struct ErrorWithNonexistentField { } #[derive(Diagnostic)] -//~^ ERROR invalid format string: expected `'}'` +//~^ ERROR invalid format string: expected `}` #[diag(no_crate_example, code = E0123)] struct ErrorMissingClosingBrace { #[suggestion(no_crate_suggestion, code = "{name")] diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 97f9896f3a7..ff7af388514 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -184,11 +184,11 @@ error: `name` doesn't refer to a field on this type LL | #[suggestion(no_crate_suggestion, code = "{name}")] | ^^^^^^^^ -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/diagnostic-derive.rs:180:10 | LL | #[derive(Diagnostic)] - | ^^^^^^^^^^ expected `'}'` in format string + | ^^^^^^^^^^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/abi/unsupported.aarch64.stderr b/tests/ui/abi/unsupported.aarch64.stderr index 123e7663257..82908ef88a8 100644 --- a/tests/ui/abi/unsupported.aarch64.stderr +++ b/tests/ui/abi/unsupported.aarch64.stderr @@ -1,55 +1,209 @@ +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:28:1 + --> $DIR/unsupported.rs:40:1 + | +LL | extern "ptx-kernel" {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:49:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:62:1 + | +LL | extern "aapcs" {} + | ^^^^^^^^^^^^^^^^^ + +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:71:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:76:1 + | +LL | extern "msp430-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:81:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:86:1 + | +LL | extern "avr-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "riscv-interrupt-m" is not supported on this target + --> $DIR/unsupported.rs:94:17 + | +LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:105:1 + | +LL | extern "riscv-interrupt-m" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "x86-interrupt" is not supported on this target + --> $DIR/unsupported.rs:116:15 + | +LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:127:1 + | +LL | extern "x86-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:139:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"thiscall"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:152:1 + | +LL | extern "thiscall" {} + | ^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:170:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: use of calling convention not supported on this target + --> $DIR/unsupported.rs:183:1 + | +LL | extern "stdcall" {} + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> + = note: `#[warn(unsupported_calling_conventions)]` on by default + +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:195:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:203:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:208:1 + | +LL | extern "C-cmse-nonsecure-entry" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:33:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:30:1 + --> $DIR/unsupported.rs:43:1 | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:36:1 + --> $DIR/unsupported.rs:69:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:38:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "riscv-interrupt-m" fn riscv() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:45:1 + --> $DIR/unsupported.rs:111:1 | LL | extern "x86-interrupt" fn x86() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:50:1 + --> $DIR/unsupported.rs:133:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:56:1 + --> $DIR/unsupported.rs:159:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> - = note: `#[warn(unsupported_calling_conventions)]` on by default -error: aborting due to 7 previous errors; 1 warning emitted +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:201:1 + | +LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors; 12 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.arm.stderr b/tests/ui/abi/unsupported.arm.stderr index 7376bb17d6b..39ec5d16fcd 100644 --- a/tests/ui/abi/unsupported.arm.stderr +++ b/tests/ui/abi/unsupported.arm.stderr @@ -1,49 +1,188 @@ +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:40:1 + | +LL | extern "ptx-kernel" {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:71:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:76:1 + | +LL | extern "msp430-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:81:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:86:1 + | +LL | extern "avr-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "riscv-interrupt-m" is not supported on this target + --> $DIR/unsupported.rs:94:17 + | +LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:105:1 + | +LL | extern "riscv-interrupt-m" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "x86-interrupt" is not supported on this target + --> $DIR/unsupported.rs:116:15 + | +LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:127:1 + | +LL | extern "x86-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:139:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"thiscall"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:152:1 + | +LL | extern "thiscall" {} + | ^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:170:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: use of calling convention not supported on this target + --> $DIR/unsupported.rs:183:1 + | +LL | extern "stdcall" {} + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> + = note: `#[warn(unsupported_calling_conventions)]` on by default + +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:195:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:203:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:208:1 + | +LL | extern "C-cmse-nonsecure-entry" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:28:1 + --> $DIR/unsupported.rs:33:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:36:1 + --> $DIR/unsupported.rs:69:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:38:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "riscv-interrupt-m" fn riscv() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:45:1 + --> $DIR/unsupported.rs:111:1 | LL | extern "x86-interrupt" fn x86() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:50:1 + --> $DIR/unsupported.rs:133:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:56:1 + --> $DIR/unsupported.rs:159:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> - = note: `#[warn(unsupported_calling_conventions)]` on by default -error: aborting due to 6 previous errors; 1 warning emitted +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:201:1 + | +LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors; 11 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.i686.stderr b/tests/ui/abi/unsupported.i686.stderr index 23b0e581887..1dc01a66aab 100644 --- a/tests/ui/abi/unsupported.i686.stderr +++ b/tests/ui/abi/unsupported.i686.stderr @@ -1,33 +1,139 @@ +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:40:1 + | +LL | extern "ptx-kernel" {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:49:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:62:1 + | +LL | extern "aapcs" {} + | ^^^^^^^^^^^^^^^^^ + +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:71:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:76:1 + | +LL | extern "msp430-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:81:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:86:1 + | +LL | extern "avr-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "riscv-interrupt-m" is not supported on this target + --> $DIR/unsupported.rs:94:17 + | +LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:105:1 + | +LL | extern "riscv-interrupt-m" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:195:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:203:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:208:1 + | +LL | extern "C-cmse-nonsecure-entry" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:28:1 + --> $DIR/unsupported.rs:33:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:30:1 + --> $DIR/unsupported.rs:43:1 | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:36:1 + --> $DIR/unsupported.rs:69:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:38:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "riscv-interrupt-m" fn riscv() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:201:1 + | +LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors; 7 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.riscv32.stderr b/tests/ui/abi/unsupported.riscv32.stderr index 708fd2c92a9..e7d5197feeb 100644 --- a/tests/ui/abi/unsupported.riscv32.stderr +++ b/tests/ui/abi/unsupported.riscv32.stderr @@ -1,49 +1,188 @@ +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:28:1 + --> $DIR/unsupported.rs:40:1 + | +LL | extern "ptx-kernel" {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:49:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:62:1 + | +LL | extern "aapcs" {} + | ^^^^^^^^^^^^^^^^^ + +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:71:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:76:1 + | +LL | extern "msp430-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:81:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:86:1 + | +LL | extern "avr-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "x86-interrupt" is not supported on this target + --> $DIR/unsupported.rs:116:15 + | +LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:127:1 + | +LL | extern "x86-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:139:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"thiscall"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:152:1 + | +LL | extern "thiscall" {} + | ^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:170:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: use of calling convention not supported on this target + --> $DIR/unsupported.rs:183:1 + | +LL | extern "stdcall" {} + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> + = note: `#[warn(unsupported_calling_conventions)]` on by default + +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:195:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:203:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:208:1 + | +LL | extern "C-cmse-nonsecure-entry" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:33:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:30:1 + --> $DIR/unsupported.rs:43:1 | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:36:1 + --> $DIR/unsupported.rs:69:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:38:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:45:1 + --> $DIR/unsupported.rs:111:1 | LL | extern "x86-interrupt" fn x86() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:50:1 + --> $DIR/unsupported.rs:133:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:56:1 + --> $DIR/unsupported.rs:159:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> - = note: `#[warn(unsupported_calling_conventions)]` on by default -error: aborting due to 6 previous errors; 1 warning emitted +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:201:1 + | +LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors; 11 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.riscv64.stderr b/tests/ui/abi/unsupported.riscv64.stderr index 708fd2c92a9..e7d5197feeb 100644 --- a/tests/ui/abi/unsupported.riscv64.stderr +++ b/tests/ui/abi/unsupported.riscv64.stderr @@ -1,49 +1,188 @@ +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:28:1 + --> $DIR/unsupported.rs:40:1 + | +LL | extern "ptx-kernel" {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:49:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:62:1 + | +LL | extern "aapcs" {} + | ^^^^^^^^^^^^^^^^^ + +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:71:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:76:1 + | +LL | extern "msp430-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:81:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:86:1 + | +LL | extern "avr-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "x86-interrupt" is not supported on this target + --> $DIR/unsupported.rs:116:15 + | +LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:127:1 + | +LL | extern "x86-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:139:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"thiscall"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:152:1 + | +LL | extern "thiscall" {} + | ^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:170:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: use of calling convention not supported on this target + --> $DIR/unsupported.rs:183:1 + | +LL | extern "stdcall" {} + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> + = note: `#[warn(unsupported_calling_conventions)]` on by default + +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:195:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:203:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:208:1 + | +LL | extern "C-cmse-nonsecure-entry" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:33:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:30:1 + --> $DIR/unsupported.rs:43:1 | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:36:1 + --> $DIR/unsupported.rs:69:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:38:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:45:1 + --> $DIR/unsupported.rs:111:1 | LL | extern "x86-interrupt" fn x86() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:50:1 + --> $DIR/unsupported.rs:133:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:56:1 + --> $DIR/unsupported.rs:159:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> - = note: `#[warn(unsupported_calling_conventions)]` on by default -error: aborting due to 6 previous errors; 1 warning emitted +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:201:1 + | +LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors; 11 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.rs b/tests/ui/abi/unsupported.rs index c12883e3fce..0eb039269a3 100644 --- a/tests/ui/abi/unsupported.rs +++ b/tests/ui/abi/unsupported.rs @@ -20,39 +20,142 @@ abi_msp430_interrupt, abi_avr_interrupt, abi_x86_interrupt, - abi_riscv_interrupt + abi_riscv_interrupt, + abi_c_cmse_nonsecure_call, + cmse_nonsecure_entry )] #[lang = "sized"] trait Sized {} +#[lang = "copy"] +trait Copy {} + extern "ptx-kernel" fn ptx() {} //~^ ERROR is not a supported ABI +fn ptx_ptr(f: extern "ptx-kernel" fn()) { + //~^ WARN unsupported_fn_ptr_calling_conventions + //~^^ WARN this was previously accepted + f() +} +extern "ptx-kernel" {} +//~^ ERROR is not a supported ABI + extern "aapcs" fn aapcs() {} //[x64]~^ ERROR is not a supported ABI //[i686]~^^ ERROR is not a supported ABI //[aarch64]~^^^ ERROR is not a supported ABI //[riscv32]~^^^^ ERROR is not a supported ABI //[riscv64]~^^^^^ ERROR is not a supported ABI +fn aapcs_ptr(f: extern "aapcs" fn()) { + //[x64]~^ WARN unsupported_fn_ptr_calling_conventions + //[x64]~^^ WARN this was previously accepted + //[i686]~^^^ WARN unsupported_fn_ptr_calling_conventions + //[i686]~^^^^ WARN this was previously accepted + //[aarch64]~^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[aarch64]~^^^^^^ WARN this was previously accepted + //[riscv32]~^^^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[riscv32]~^^^^^^^^ WARN this was previously accepted + //[riscv64]~^^^^^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[riscv64]~^^^^^^^^^^ WARN this was previously accepted + f() +} +extern "aapcs" {} +//[x64]~^ ERROR is not a supported ABI +//[i686]~^^ ERROR is not a supported ABI +//[aarch64]~^^^ ERROR is not a supported ABI +//[riscv32]~^^^^ ERROR is not a supported ABI +//[riscv64]~^^^^^ ERROR is not a supported ABI + extern "msp430-interrupt" fn msp430() {} //~^ ERROR is not a supported ABI +fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + //~^ WARN unsupported_fn_ptr_calling_conventions + //~^^ WARN this was previously accepted + f() +} +extern "msp430-interrupt" {} +//~^ ERROR is not a supported ABI + extern "avr-interrupt" fn avr() {} //~^ ERROR is not a supported ABI +fn avr_ptr(f: extern "avr-interrupt" fn()) { + //~^ WARN unsupported_fn_ptr_calling_conventions + //~^^ WARN this was previously accepted + f() +} +extern "avr-interrupt" {} +//~^ ERROR is not a supported ABI + extern "riscv-interrupt-m" fn riscv() {} //[arm]~^ ERROR is not a supported ABI //[x64]~^^ ERROR is not a supported ABI //[i686]~^^^ ERROR is not a supported ABI //[aarch64]~^^^^ ERROR is not a supported ABI +fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { + //[arm]~^ WARN unsupported_fn_ptr_calling_conventions + //[arm]~^^ WARN this was previously accepted + //[x64]~^^^ WARN unsupported_fn_ptr_calling_conventions + //[x64]~^^^^ WARN this was previously accepted + //[i686]~^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[i686]~^^^^^^ WARN this was previously accepted + //[aarch64]~^^^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[aarch64]~^^^^^^^^ WARN this was previously accepted + f() +} +extern "riscv-interrupt-m" {} +//[arm]~^ ERROR is not a supported ABI +//[x64]~^^ ERROR is not a supported ABI +//[i686]~^^^ ERROR is not a supported ABI +//[aarch64]~^^^^ ERROR is not a supported ABI + extern "x86-interrupt" fn x86() {} //[aarch64]~^ ERROR is not a supported ABI //[arm]~^^ ERROR is not a supported ABI //[riscv32]~^^^ ERROR is not a supported ABI //[riscv64]~^^^^ ERROR is not a supported ABI +fn x86_ptr(f: extern "x86-interrupt" fn()) { + //[aarch64]~^ WARN unsupported_fn_ptr_calling_conventions + //[aarch64]~^^ WARN this was previously accepted + //[arm]~^^^ WARN unsupported_fn_ptr_calling_conventions + //[arm]~^^^^ WARN this was previously accepted + //[riscv32]~^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[riscv32]~^^^^^^ WARN this was previously accepted + //[riscv64]~^^^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[riscv64]~^^^^^^^^ WARN this was previously accepted + f() +} +extern "x86-interrupt" {} +//[aarch64]~^ ERROR is not a supported ABI +//[arm]~^^ ERROR is not a supported ABI +//[riscv32]~^^^ ERROR is not a supported ABI +//[riscv64]~^^^^ ERROR is not a supported ABI + extern "thiscall" fn thiscall() {} //[x64]~^ ERROR is not a supported ABI //[arm]~^^ ERROR is not a supported ABI //[aarch64]~^^^ ERROR is not a supported ABI //[riscv32]~^^^^ ERROR is not a supported ABI //[riscv64]~^^^^^ ERROR is not a supported ABI +fn thiscall_ptr(f: extern "thiscall" fn()) { + //[x64]~^ WARN unsupported_fn_ptr_calling_conventions + //[x64]~^^ WARN this was previously accepted + //[arm]~^^^ WARN unsupported_fn_ptr_calling_conventions + //[arm]~^^^^ WARN this was previously accepted + //[aarch64]~^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[aarch64]~^^^^^^ WARN this was previously accepted + //[riscv32]~^^^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[riscv32]~^^^^^^^^ WARN this was previously accepted + //[riscv64]~^^^^^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[riscv64]~^^^^^^^^^^ WARN this was previously accepted + f() +} +extern "thiscall" {} +//[x64]~^ ERROR is not a supported ABI +//[arm]~^^ ERROR is not a supported ABI +//[aarch64]~^^^ ERROR is not a supported ABI +//[riscv32]~^^^^ ERROR is not a supported ABI +//[riscv64]~^^^^^ ERROR is not a supported ABI + extern "stdcall" fn stdcall() {} //[x64]~^ WARN use of calling convention not supported //[x64]~^^ WARN this was previously accepted @@ -64,3 +167,43 @@ extern "stdcall" fn stdcall() {} //[riscv32]~^^^^^^^^ WARN this was previously accepted //[riscv64]~^^^^^^^^^ WARN use of calling convention not supported //[riscv64]~^^^^^^^^^^ WARN this was previously accepted +fn stdcall_ptr(f: extern "stdcall" fn()) { + //[x64]~^ WARN unsupported_fn_ptr_calling_conventions + //[x64]~^^ WARN this was previously accepted + //[arm]~^^^ WARN unsupported_fn_ptr_calling_conventions + //[arm]~^^^^ WARN this was previously accepted + //[aarch64]~^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[aarch64]~^^^^^^ WARN this was previously accepted + //[riscv32]~^^^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[riscv32]~^^^^^^^^ WARN this was previously accepted + //[riscv64]~^^^^^^^^^ WARN unsupported_fn_ptr_calling_conventions + //[riscv64]~^^^^^^^^^^ WARN this was previously accepted + f() +} +extern "stdcall" {} +//[x64]~^ WARN use of calling convention not supported +//[x64]~^^ WARN this was previously accepted +//[arm]~^^^ WARN use of calling convention not supported +//[arm]~^^^^ WARN this was previously accepted +//[aarch64]~^^^^^ WARN use of calling convention not supported +//[aarch64]~^^^^^^ WARN this was previously accepted +//[riscv32]~^^^^^^^ WARN use of calling convention not supported +//[riscv32]~^^^^^^^^ WARN this was previously accepted +//[riscv64]~^^^^^^^^^ WARN use of calling convention not supported +//[riscv64]~^^^^^^^^^^ WARN this was previously accepted + +fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + //~^ WARN unsupported_fn_ptr_calling_conventions + //~^^ WARN this was previously accepted + f() +} + +extern "C-cmse-nonsecure-entry" fn cmse_entry() {} +//~^ ERROR is not a supported ABI +fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + //~^ WARN unsupported_fn_ptr_calling_conventions + //~^^ WARN this was previously accepted + f() +} +extern "C-cmse-nonsecure-entry" {} +//~^ ERROR is not a supported ABI diff --git a/tests/ui/abi/unsupported.x64.stderr b/tests/ui/abi/unsupported.x64.stderr index 7b918a948d3..bbca754dd41 100644 --- a/tests/ui/abi/unsupported.x64.stderr +++ b/tests/ui/abi/unsupported.x64.stderr @@ -1,49 +1,188 @@ +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:28:1 + --> $DIR/unsupported.rs:40:1 + | +LL | extern "ptx-kernel" {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:49:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:62:1 + | +LL | extern "aapcs" {} + | ^^^^^^^^^^^^^^^^^ + +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:71:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:76:1 + | +LL | extern "msp430-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:81:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:86:1 + | +LL | extern "avr-interrupt" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "riscv-interrupt-m" is not supported on this target + --> $DIR/unsupported.rs:94:17 + | +LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:105:1 + | +LL | extern "riscv-interrupt-m" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:139:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"thiscall"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:152:1 + | +LL | extern "thiscall" {} + | ^^^^^^^^^^^^^^^^^^^^ + +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:170:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: use of calling convention not supported on this target + --> $DIR/unsupported.rs:183:1 + | +LL | extern "stdcall" {} + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> + = note: `#[warn(unsupported_calling_conventions)]` on by default + +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:195:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:203:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260> + +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:208:1 + | +LL | extern "C-cmse-nonsecure-entry" {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:33:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:30:1 + --> $DIR/unsupported.rs:43:1 | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:36:1 + --> $DIR/unsupported.rs:69:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:38:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "riscv-interrupt-m" fn riscv() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:50:1 + --> $DIR/unsupported.rs:133:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:56:1 + --> $DIR/unsupported.rs:159:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678> - = note: `#[warn(unsupported_calling_conventions)]` on by default -error: aborting due to 6 previous errors; 1 warning emitted +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:201:1 + | +LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors; 11 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/asm/aarch64/aarch64-sve.rs b/tests/ui/asm/aarch64/aarch64-sve.rs new file mode 100644 index 00000000000..8cdc9dd4266 --- /dev/null +++ b/tests/ui/asm/aarch64/aarch64-sve.rs @@ -0,0 +1,28 @@ +//@ only-aarch64 +//@ build-pass +//@ needs-asm-support + +#![crate_type = "rlib"] +#![feature(no_core, rustc_attrs, lang_items)] +#![no_core] + +// AArch64 test corresponding to arm64ec-sve.rs. + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +impl Copy for f64 {} + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} + +fn f(x: f64) { + unsafe { + asm!("", out("p0") _); + asm!("", out("ffr") _); + } +} diff --git a/tests/ui/asm/aarch64/arm64ec-sve.rs b/tests/ui/asm/aarch64/arm64ec-sve.rs new file mode 100644 index 00000000000..389b365a754 --- /dev/null +++ b/tests/ui/asm/aarch64/arm64ec-sve.rs @@ -0,0 +1,31 @@ +//@ compile-flags: --target arm64ec-pc-windows-msvc +//@ needs-asm-support +//@ needs-llvm-components: aarch64 + +#![crate_type = "rlib"] +#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)] +#![no_core] + +// SVE cannot be used for Arm64EC +// https://github.com/rust-lang/rust/pull/131332#issuecomment-2401189142 + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +impl Copy for f64 {} + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} + +fn f(x: f64) { + unsafe { + asm!("", out("p0") _); + //~^ ERROR cannot use register `p0` + asm!("", out("ffr") _); + //~^ ERROR cannot use register `ffr` + } +} diff --git a/tests/ui/asm/aarch64/arm64ec-sve.stderr b/tests/ui/asm/aarch64/arm64ec-sve.stderr new file mode 100644 index 00000000000..3e1a5d57004 --- /dev/null +++ b/tests/ui/asm/aarch64/arm64ec-sve.stderr @@ -0,0 +1,14 @@ +error: cannot use register `p0`: x13, x14, x23, x24, x28, v16-v31, p*, ffr cannot be used for Arm64EC + --> $DIR/arm64ec-sve.rs:26:18 + | +LL | asm!("", out("p0") _); + | ^^^^^^^^^^^ + +error: cannot use register `ffr`: x13, x14, x23, x24, x28, v16-v31, p*, ffr cannot be used for Arm64EC + --> $DIR/arm64ec-sve.rs:28:18 + | +LL | asm!("", out("ffr") _); + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.rs b/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.rs index 7df042d5f88..c8bb0ebd574 100644 --- a/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.rs +++ b/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.rs @@ -2,28 +2,24 @@ //@ edition: 2021 fn f(_: impl Trait<T = Copy>) {} -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object //~| HELP you might have meant to write a bound here -//~| ERROR the trait `Copy` cannot be made into an object fn g(_: impl Trait<T = std::fmt::Debug + Eq>) {} -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object //~| HELP you might have meant to write a bound here -//~| ERROR only auto traits can be used as additional traits in a trait object -//~| HELP consider creating a new trait -//~| ERROR the trait `Eq` cannot be made into an object fn h(_: impl Trait<T<> = 'static + for<'a> Fn(&'a ())>) {} -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object //~| HELP you might have meant to write a bound here // Don't suggest assoc ty bound in trait object types, that's not valid: type Obj = dyn Trait<T = Clone>; -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object trait Trait { type T; } diff --git a/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.stderr b/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.stderr index bec60187e42..dbe285c5310 100644 --- a/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.stderr +++ b/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.stderr @@ -1,41 +1,10 @@ -error[E0038]: the trait `Copy` cannot be made into an object - --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:4:20 - | -LL | fn f(_: impl Trait<T = Copy>) {} - | ^^^^^^^^ `Copy` cannot be made into an object - | - = note: the trait cannot be made into an object because it requires `Self: Sized` - = note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - -error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:10:42 - | -LL | fn g(_: impl Trait<T = std::fmt::Debug + Eq>) {} - | --------------- ^^ additional non-auto trait - | | - | first non-auto trait - | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Debug + Eq {}` - = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits> - -error[E0038]: the trait `Eq` cannot be made into an object - --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:10:24 - | -LL | fn g(_: impl Trait<T = std::fmt::Debug + Eq>) {} - | ^^^^^^^^^^^^^^^^^^^^ `Eq` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $SRC_DIR/core/src/cmp.rs:LL:COL - | - = note: the trait cannot be made into an object because it uses `Self` as a type parameter - -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:4:24 | LL | fn f(_: impl Trait<T = Copy>) {} | ^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | fn f(_: impl Trait<T = dyn Copy>) {} | +++ @@ -44,13 +13,13 @@ help: you might have meant to write a bound here LL | fn f(_: impl Trait<T: Copy>) {} | ~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:10:24 +error[E0782]: expected a type, found a trait + --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:9:24 | LL | fn g(_: impl Trait<T = std::fmt::Debug + Eq>) {} | ^^^^^^^^^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | fn g(_: impl Trait<T = dyn std::fmt::Debug + Eq>) {} | +++ @@ -59,13 +28,13 @@ help: you might have meant to write a bound here LL | fn g(_: impl Trait<T: std::fmt::Debug + Eq>) {} | ~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:18:26 +error[E0782]: expected a type, found a trait + --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:14:26 | LL | fn h(_: impl Trait<T<> = 'static + for<'a> Fn(&'a ())>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | fn h(_: impl Trait<T<> = dyn 'static + for<'a> Fn(&'a ())>) {} | +++ @@ -74,18 +43,17 @@ help: you might have meant to write a bound here LL | fn h(_: impl Trait<T<>: 'static + for<'a> Fn(&'a ())>) {} | ~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:24:26 +error[E0782]: expected a type, found a trait + --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:20:26 | LL | type Obj = dyn Trait<T = Clone>; | ^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | type Obj = dyn Trait<T = dyn Clone>; | +++ -error: aborting due to 7 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0038, E0225, E0782. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/async-await/pin-sugar-ambiguity.rs b/tests/ui/async-await/pin-sugar-ambiguity.rs new file mode 100644 index 00000000000..d183000931e --- /dev/null +++ b/tests/ui/async-await/pin-sugar-ambiguity.rs @@ -0,0 +1,15 @@ +//@ check-pass +#![feature(pin_ergonomics)] +#![allow(dead_code, incomplete_features)] + +// Handle the case where there's ambiguity between pin as a contextual keyword and pin as a path. + +struct Foo; + +mod pin { + pub struct Foo; +} + +fn main() { + let _x: &pin ::Foo = &pin::Foo; +} diff --git a/tests/ui/async-await/pin-sugar-no-const.rs b/tests/ui/async-await/pin-sugar-no-const.rs new file mode 100644 index 00000000000..dd6456b6034 --- /dev/null +++ b/tests/ui/async-await/pin-sugar-no-const.rs @@ -0,0 +1,8 @@ +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +// Makes sure we don't accidentally accept `&pin Foo` without the `const` keyword. + +fn main() { + let _x: &pin i32 = todo!(); //~ ERROR found `i32` +} diff --git a/tests/ui/async-await/pin-sugar-no-const.stderr b/tests/ui/async-await/pin-sugar-no-const.stderr new file mode 100644 index 00000000000..5f01156c1f0 --- /dev/null +++ b/tests/ui/async-await/pin-sugar-no-const.stderr @@ -0,0 +1,15 @@ +error: expected one of `!`, `(`, `::`, `;`, `<`, or `=`, found `i32` + --> $DIR/pin-sugar-no-const.rs:7:18 + | +LL | let _x: &pin i32 = todo!(); + | - ^^^ expected one of `!`, `(`, `::`, `;`, `<`, or `=` + | | + | while parsing the type for `_x` + | +help: there is a keyword `in` with a similar name + | +LL | let _x: &in i32 = todo!(); + | ~~ + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/pin-sugar.rs b/tests/ui/async-await/pin-sugar.rs new file mode 100644 index 00000000000..8dbdec418b1 --- /dev/null +++ b/tests/ui/async-await/pin-sugar.rs @@ -0,0 +1,51 @@ +//@ check-pass + +#![feature(pin_ergonomics)] +#![allow(dead_code, incomplete_features)] + +// Makes sure we can handle `&pin mut T` and `&pin const T` as sugar for `Pin<&mut T>` and +// `Pin<&T>`. + +use std::pin::Pin; + +struct Foo; + +impl Foo { + fn baz(self: &pin mut Self) { + } + + fn baz_const(self: &pin const Self) { + } + + fn baz_lt<'a>(self: &'a pin mut Self) { + } + + fn baz_const_lt(self: &'_ pin const Self) { + } +} + +fn foo(_: &pin mut Foo) { +} + +fn foo_const(x: &pin const Foo) { +} + +fn bar(x: &pin mut Foo) { + foo(x); + foo(x); // for this to work we need to automatically reborrow, + // as if the user had written `foo(x.as_mut())`. + + Foo::baz(x); + Foo::baz(x); + + // make sure we can reborrow &mut as &. + foo_const(x); + Foo::baz_const(x); + + let x: &pin const _ = Pin::new(&Foo); + + foo_const(x); // make sure reborrowing from & to & works. + foo_const(x); +} + +fn main() {} diff --git a/tests/ui/autodiff/autodiff_illegal.rs b/tests/ui/autodiff/autodiff_illegal.rs new file mode 100644 index 00000000000..c0548d2bbb8 --- /dev/null +++ b/tests/ui/autodiff/autodiff_illegal.rs @@ -0,0 +1,160 @@ +//@ needs-enzyme + +#![feature(autodiff)] +//@ pretty-mode:expanded +//@ pretty-compare-only +//@ pp-exact:autodiff_illegal.pp + +// Test that invalid ad macros give nice errors and don't ICE. + +use std::autodiff::autodiff; + +// We can't use Duplicated on scalars +#[autodiff(df1, Reverse, Duplicated)] +pub fn f1(x: f64) { +//~^ ERROR Duplicated can not be used for this type + unimplemented!() +} + +// Too many activities +#[autodiff(df3, Reverse, Duplicated, Const)] +pub fn f3(x: f64) { +//~^^ ERROR expected 1 activities, but found 2 + unimplemented!() +} + +// To few activities +#[autodiff(df4, Reverse)] +pub fn f4(x: f64) { +//~^^ ERROR expected 1 activities, but found 0 + unimplemented!() +} + +// We can't use Dual in Reverse mode +#[autodiff(df5, Reverse, Dual)] +pub fn f5(x: f64) { +//~^^ ERROR Dual can not be used in Reverse Mode + unimplemented!() +} + +// We can't use Duplicated in Forward mode +#[autodiff(df6, Forward, Duplicated)] +pub fn f6(x: f64) { +//~^^ ERROR Duplicated can not be used in Forward Mode +//~^^ ERROR Duplicated can not be used for this type + unimplemented!() +} + +fn dummy() { + + #[autodiff(df7, Forward, Dual)] + let mut x = 5; + //~^ ERROR autodiff must be applied to function + + #[autodiff(df7, Forward, Dual)] + x = x + 3; + //~^^ ERROR attributes on expressions are experimental [E0658] + //~^^ ERROR autodiff must be applied to function + + #[autodiff(df7, Forward, Dual)] + let add_one_v2 = |x: u32| -> u32 { x + 1 }; + //~^ ERROR autodiff must be applied to function +} + +// Malformed, where args? +#[autodiff] +pub fn f7(x: f64) { +//~^ ERROR autodiff must be applied to function + unimplemented!() +} + +// Malformed, where args? +#[autodiff()] +pub fn f8(x: f64) { +//~^ ERROR autodiff requires at least a name and mode + unimplemented!() +} + +// Invalid attribute syntax +#[autodiff = ""] +pub fn f9(x: f64) { +//~^ ERROR autodiff must be applied to function + unimplemented!() +} + +fn fn_exists() {} + +// We colide with an already existing function +#[autodiff(fn_exists, Reverse, Active)] +pub fn f10(x: f64) { +//~^^ ERROR the name `fn_exists` is defined multiple times [E0428] + unimplemented!() +} + +// Malformed, missing a mode +#[autodiff(df11)] +pub fn f11() { +//~^ ERROR autodiff requires at least a name and mode + unimplemented!() +} + +// Invalid Mode +#[autodiff(df12, Debug)] +pub fn f12() { +//~^^ ERROR unknown Mode: `Debug`. Use `Forward` or `Reverse` + unimplemented!() +} + +// Invalid, please pick one Mode +// or use two autodiff macros. +#[autodiff(df13, Forward, Reverse)] +pub fn f13() { +//~^^ ERROR did not recognize Activity: `Reverse` + unimplemented!() +} + +struct Foo {} + +// We can't handle Active structs, because that would mean (in the general case), that we would +// need to allocate and initialize arbitrary user types. We have Duplicated/Dual input args for +// that. FIXME: Give a nicer error and suggest to the user to have a `&mut Foo` input instead. +#[autodiff(df14, Reverse, Active, Active)] +fn f14(x: f32) -> Foo { + unimplemented!() +} + +type MyFloat = f32; + +// We would like to support type alias to f32/f64 in argument type in the future, +// but that requires us to implement our checks at a later stage +// like THIR which has type information available. +#[autodiff(df15, Reverse, Active, Active)] +fn f15(x: MyFloat) -> f32 { +//~^^ ERROR failed to resolve: use of undeclared type `MyFloat` [E0433] + unimplemented!() +} + +// We would like to support type alias to f32/f64 in return type in the future +#[autodiff(df16, Reverse, Active, Active)] +fn f16(x: f32) -> MyFloat { + unimplemented!() +} + +#[repr(transparent)] +struct F64Trans { inner: f64 } + +// We would like to support `#[repr(transparent)]` f32/f64 wrapper in return type in the future +#[autodiff(df17, Reverse, Active, Active)] +fn f17(x: f64) -> F64Trans { + unimplemented!() +} + +// We would like to support `#[repr(transparent)]` f32/f64 wrapper in argument type in the future +#[autodiff(df18, Reverse, Active, Active)] +fn f18(x: F64Trans) -> f64 { + //~^^ ERROR failed to resolve: use of undeclared type `F64Trans` [E0433] + unimplemented!() +} + + +fn main() {} diff --git a/tests/ui/autodiff/autodiff_illegal.stderr b/tests/ui/autodiff/autodiff_illegal.stderr new file mode 100644 index 00000000000..3a7242b2f5d --- /dev/null +++ b/tests/ui/autodiff/autodiff_illegal.stderr @@ -0,0 +1,152 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/autodiff_illegal.rs:54:5 + | +LL | #[autodiff(df7, Forward, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: Duplicated can not be used for this type + --> $DIR/autodiff_illegal.rs:14:14 + | +LL | pub fn f1(x: f64) { + | ^^^ + +error: expected 1 activities, but found 2 + --> $DIR/autodiff_illegal.rs:20:1 + | +LL | #[autodiff(df3, Reverse, Duplicated, Const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected 1 activities, but found 0 + --> $DIR/autodiff_illegal.rs:27:1 + | +LL | #[autodiff(df4, Reverse)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: Dual can not be used in Reverse Mode + --> $DIR/autodiff_illegal.rs:34:1 + | +LL | #[autodiff(df5, Reverse, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: Duplicated can not be used in Forward Mode + --> $DIR/autodiff_illegal.rs:41:1 + | +LL | #[autodiff(df6, Forward, Duplicated)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: Duplicated can not be used for this type + --> $DIR/autodiff_illegal.rs:42:14 + | +LL | pub fn f6(x: f64) { + | ^^^ + +error: autodiff must be applied to function + --> $DIR/autodiff_illegal.rs:51:5 + | +LL | let mut x = 5; + | ^^^^^^^^^^^^^^ + +error: autodiff must be applied to function + --> $DIR/autodiff_illegal.rs:55:5 + | +LL | x = x + 3; + | ^ + +error: autodiff must be applied to function + --> $DIR/autodiff_illegal.rs:60:5 + | +LL | let add_one_v2 = |x: u32| -> u32 { x + 1 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: autodiff must be applied to function + --> $DIR/autodiff_illegal.rs:66:1 + | +LL | / pub fn f7(x: f64) { +LL | | +LL | | unimplemented!() +LL | | } + | |_^ + +error: autodiff requires at least a name and mode + --> $DIR/autodiff_illegal.rs:73:1 + | +LL | / pub fn f8(x: f64) { +LL | | +LL | | unimplemented!() +LL | | } + | |_^ + +error: autodiff must be applied to function + --> $DIR/autodiff_illegal.rs:80:1 + | +LL | / pub fn f9(x: f64) { +LL | | +LL | | unimplemented!() +LL | | } + | |_^ + +error[E0428]: the name `fn_exists` is defined multiple times + --> $DIR/autodiff_illegal.rs:88:1 + | +LL | fn fn_exists() {} + | -------------- previous definition of the value `fn_exists` here +... +LL | #[autodiff(fn_exists, Reverse, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fn_exists` redefined here + | + = note: `fn_exists` must be defined only once in the value namespace of this module + = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: autodiff requires at least a name and mode + --> $DIR/autodiff_illegal.rs:96:1 + | +LL | / pub fn f11() { +LL | | +LL | | unimplemented!() +LL | | } + | |_^ + +error: unknown Mode: `Debug`. Use `Forward` or `Reverse` + --> $DIR/autodiff_illegal.rs:102:18 + | +LL | #[autodiff(df12, Debug)] + | ^^^^^ + +error: did not recognize Activity: `Reverse` + --> $DIR/autodiff_illegal.rs:110:27 + | +LL | #[autodiff(df13, Forward, Reverse)] + | ^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared type `MyFloat` + --> $DIR/autodiff_illegal.rs:131:1 + | +LL | #[autodiff(df15, Reverse, Active, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat` + | + = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0433]: failed to resolve: use of undeclared type `F64Trans` + --> $DIR/autodiff_illegal.rs:153:1 + | +LL | #[autodiff(df18, Reverse, Active, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans` + | + = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 19 previous errors + +Some errors have detailed explanations: E0428, E0433, E0658. +For more information about an error, try `rustc --explain E0428`. diff --git a/tests/ui/autodiff/auxiliary/my_macro.rs b/tests/ui/autodiff/auxiliary/my_macro.rs new file mode 100644 index 00000000000..417199611cc --- /dev/null +++ b/tests/ui/autodiff/auxiliary/my_macro.rs @@ -0,0 +1,12 @@ +//@ force-host +//@ no-prefer-dynamic +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +#[macro_use] +pub fn autodiff(_attr: TokenStream, item: TokenStream) -> TokenStream { + item // identity proc-macro +} diff --git a/tests/ui/autodiff/visibility.rs b/tests/ui/autodiff/visibility.rs new file mode 100644 index 00000000000..6a4851de2dc --- /dev/null +++ b/tests/ui/autodiff/visibility.rs @@ -0,0 +1,17 @@ +//@ ignore-enzyme +//@ revisions: std_autodiff no_std_autodiff +//@[no_std_autodiff] check-pass +//@ aux-build: my_macro.rs +#![crate_type = "lib"] +#![feature(autodiff)] + +#[cfg(std_autodiff)] +use std::autodiff::autodiff; + +extern crate my_macro; +use my_macro::autodiff; // bring `autodiff` in scope + +#[autodiff] +//[std_autodiff]~^^^ ERROR the name `autodiff` is defined multiple times +//[std_autodiff]~^^ ERROR this rustc version does not support autodiff +fn foo() {} diff --git a/tests/ui/autodiff/visibility.std_autodiff.stderr b/tests/ui/autodiff/visibility.std_autodiff.stderr new file mode 100644 index 00000000000..720c9a00170 --- /dev/null +++ b/tests/ui/autodiff/visibility.std_autodiff.stderr @@ -0,0 +1,24 @@ +error[E0252]: the name `autodiff` is defined multiple times + --> $DIR/visibility.rs:12:5 + | +LL | use std::autodiff::autodiff; + | ----------------------- previous import of the macro `autodiff` here +... +LL | use my_macro::autodiff; // bring `autodiff` in scope + | ^^^^^^^^^^^^^^^^^^ `autodiff` reimported here + | + = note: `autodiff` must be defined only once in the macro namespace of this module +help: you can use `as` to change the binding name of the import + | +LL | use my_macro::autodiff as other_autodiff; // bring `autodiff` in scope + | +++++++++++++++++ + +error: this rustc version does not support autodiff + --> $DIR/visibility.rs:14:1 + | +LL | #[autodiff] + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0252`. diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs index fce6210b2f4..d47a8e085fd 100644 --- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs +++ b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs @@ -1,3 +1,5 @@ +//@ only-x86_64 + fn efiapi(f: extern "efiapi" fn(usize, ...)) { //~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl` //~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr index 94e9628f0f0..41be3784245 100644 --- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr +++ b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr @@ -1,5 +1,5 @@ error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable - --> $DIR/feature-gate-extended_varargs_abi_support.rs:1:14 + --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14 | LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,13 +9,13 @@ LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl` - --> $DIR/feature-gate-extended_varargs_abi_support.rs:1:14 + --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14 | LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable - --> $DIR/feature-gate-extended_varargs_abi_support.rs:6:12 + --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12 | LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,13 +25,13 @@ LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl` - --> $DIR/feature-gate-extended_varargs_abi_support.rs:6:12 + --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12 | LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable - --> $DIR/feature-gate-extended_varargs_abi_support.rs:11:11 + --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11 | LL | fn win(f: extern "win64" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | fn win(f: extern "win64" fn(usize, ...)) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl` - --> $DIR/feature-gate-extended_varargs_abi_support.rs:11:11 + --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11 | LL | fn win(f: extern "win64" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention diff --git a/tests/ui/c-variadic/variadic-ffi-2-arm.rs b/tests/ui/c-variadic/variadic-ffi-2-arm.rs new file mode 100644 index 00000000000..3b0a71007a0 --- /dev/null +++ b/tests/ui/c-variadic/variadic-ffi-2-arm.rs @@ -0,0 +1,9 @@ +//@ only-arm +//@ build-pass +#![feature(extended_varargs_abi_support)] + +fn aapcs(f: extern "aapcs" fn(usize, ...)) { + f(22, 44); +} + +fn main() {} diff --git a/tests/ui/c-variadic/variadic-ffi-2.rs b/tests/ui/c-variadic/variadic-ffi-2.rs index a7261ebe936..bafb7e2b20c 100644 --- a/tests/ui/c-variadic/variadic-ffi-2.rs +++ b/tests/ui/c-variadic/variadic-ffi-2.rs @@ -1,6 +1,7 @@ //@ ignore-arm stdcall isn't supported #![feature(extended_varargs_abi_support)] +#[allow(unsupported_fn_ptr_calling_conventions)] fn baz(f: extern "stdcall" fn(usize, ...)) { //~^ ERROR: C-variadic function must have a compatible calling convention, // like C, cdecl, system, aapcs, win64, sysv64 or efiapi @@ -10,15 +11,22 @@ fn baz(f: extern "stdcall" fn(usize, ...)) { fn system(f: extern "system" fn(usize, ...)) { f(22, 44); } -fn aapcs(f: extern "aapcs" fn(usize, ...)) { - f(22, 44); -} +#[cfg(target_arch = "x86_64")] fn sysv(f: extern "sysv64" fn(usize, ...)) { f(22, 44); } +#[cfg(target_arch = "x86_64")] fn win(f: extern "win64" fn(usize, ...)) { f(22, 44); } +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "x86", + target_arch = "x86_64" +))] fn efiapi(f: extern "efiapi" fn(usize, ...)) { f(22, 44); } diff --git a/tests/ui/c-variadic/variadic-ffi-2.stderr b/tests/ui/c-variadic/variadic-ffi-2.stderr index fbf273b1f1d..e52de93a926 100644 --- a/tests/ui/c-variadic/variadic-ffi-2.stderr +++ b/tests/ui/c-variadic/variadic-ffi-2.stderr @@ -1,5 +1,5 @@ error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi` - --> $DIR/variadic-ffi-2.rs:4:11 + --> $DIR/variadic-ffi-2.rs:5:11 | LL | fn baz(f: extern "stdcall" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs index e05dbf3bbc4..2d0ed5d2a30 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs @@ -1,8 +1,9 @@ // gate-test-abi_c_cmse_nonsecure_call +#[allow(unsupported_fn_ptr_calling_conventions)] fn main() { let non_secure_function = unsafe { core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>( - //~^ ERROR [E0658] + //~^ ERROR [E0658] 0x10000004, ) }; diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr index 64e9b7cc639..120d5cc5293 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr @@ -1,5 +1,5 @@ error[E0658]: C-cmse-nonsecure-call ABI is experimental and subject to change - --> $DIR/gate_test.rs:4:46 + --> $DIR/gate_test.rs:5:46 | LL | core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>( | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs new file mode 100644 index 00000000000..a264bba6f3c --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -0,0 +1,77 @@ +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(cmse_nonsecure_entry, c_variadic, no_core, lang_items)] +#![no_core] +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +impl Copy for u32 {} + +#[repr(C)] +struct Wrapper<T>(T); + +impl<T: Copy> Wrapper<T> { + extern "C-cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 { + //~^ ERROR [E0798] + 0 + } + + extern "C-cmse-nonsecure-entry" fn ambient_generic_nested( + //~^ ERROR [E0798] + _: Wrapper<T>, + _: u32, + _: u32, + _: u32, + ) -> u64 { + 0 + } +} + +extern "C-cmse-nonsecure-entry" fn introduced_generic<U: Copy>( + //~^ ERROR [E0798] + _: U, + _: u32, + _: u32, + _: u32, +) -> u64 { + 0 +} + +extern "C-cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 { + //~^ ERROR [E0798] + 0 +} + +extern "C-cmse-nonsecure-entry" fn reference(x: &usize) -> usize { + *x +} + +trait Trait {} + +extern "C-cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait { + //~^ ERROR return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers [E0798] + x +} + +extern "C-cmse-nonsecure-entry" fn static_trait_object( + x: &'static dyn Trait, +) -> &'static dyn Trait { + //~^ ERROR return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers [E0798] + x +} + +#[repr(transparent)] +struct WrapperTransparent<'a>(&'a dyn Trait); + +extern "C-cmse-nonsecure-entry" fn wrapped_trait_object( + x: WrapperTransparent, +) -> WrapperTransparent { + //~^ ERROR return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers [E0798] + x +} + +extern "C-cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~| ERROR requires `va_list` lang_item +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr new file mode 100644 index 00000000000..9e67f881f75 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -0,0 +1,78 @@ +error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + --> $DIR/generics.rs:74:55 + | +LL | extern "C-cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ^^^^^^ + +error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:31:1 + | +LL | / extern "C-cmse-nonsecure-entry" fn introduced_generic<U: Copy>( +LL | | +LL | | _: U, +LL | | _: u32, +LL | | _: u32, +LL | | _: u32, +LL | | ) -> u64 { + | |________^ + +error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:41:1 + | +LL | extern "C-cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:15:5 + | +LL | extern "C-cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:20:5 + | +LL | / extern "C-cmse-nonsecure-entry" fn ambient_generic_nested( +LL | | +LL | | _: Wrapper<T>, +LL | | _: u32, +LL | | _: u32, +LL | | _: u32, +LL | | ) -> u64 { + | |____________^ + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/generics.rs:52:67 + | +LL | extern "C-cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait { + | ^^^^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/generics.rs:59:6 + | +LL | ) -> &'static dyn Trait { + | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/generics.rs:69:6 + | +LL | ) -> WrapperTransparent { + | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error: requires `va_list` lang_item + --> $DIR/generics.rs:74:55 + | +LL | extern "C-cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ^^^^^^ + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-registers.rs deleted file mode 100644 index de6888fae62..00000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-registers.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ build-pass -//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -//@ needs-llvm-components: arm -#![feature(cmse_nonsecure_entry, no_core, lang_items)] -#![no_core] -#![crate_type = "lib"] -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} -impl Copy for u32 {} - -#[no_mangle] -pub extern "C-cmse-nonsecure-entry" fn entry_function(_: u32, _: u32, _: u32, d: u32) -> u32 { - d -} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.rs deleted file mode 100644 index 4413c461c04..00000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.rs +++ /dev/null @@ -1,21 +0,0 @@ -//@ build-fail -//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -//@ needs-llvm-components: arm -#![feature(cmse_nonsecure_entry, no_core, lang_items)] -#![no_core] -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} -impl Copy for u32 {} - -#[no_mangle] -pub extern "C-cmse-nonsecure-entry" fn entry_function( - _: u32, - _: u32, - _: u32, - _: u32, - e: u32, -) -> u32 { - e -} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.stderr deleted file mode 100644 index cfbdda509e5..00000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.stderr +++ /dev/null @@ -1,4 +0,0 @@ -error: <unknown>:0:0: in function entry_function i32 (i32, i32, i32, i32, i32): secure entry function requires arguments on stack - -error: aborting due to 1 previous error - diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs new file mode 100644 index 00000000000..572d792d5a5 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs @@ -0,0 +1,26 @@ +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(cmse_nonsecure_entry, no_core, lang_items)] +#![no_core] +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} +impl Copy for u32 {} + +#[repr(C, align(16))] +#[allow(unused)] +pub struct AlignRelevant(u32); + +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {} //~ ERROR [E0798] +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn f2(_: u32, _: u32, _: u32, _: u16, _: u16) {} //~ ERROR [E0798] +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {} //~ ERROR [E0798] +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {} //~ ERROR [E0798] + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C-cmse-nonsecure-entry" fn f5(_: [u32; 5]) {} //~ ERROR [E0798] diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr new file mode 100644 index 00000000000..b77e64c6bfb --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr @@ -0,0 +1,43 @@ +error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/params-via-stack.rs:16:78 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {} + | ^^^^^^^^^^^ these arguments don't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + +error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/params-via-stack.rs:18:78 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f2(_: u32, _: u32, _: u32, _: u16, _: u16) {} + | ^^^ this argument doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + +error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/params-via-stack.rs:20:62 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {} + | ^^^ this argument doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + +error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/params-via-stack.rs:22:64 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {} + | ^^^ this argument doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + +error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/params-via-stack.rs:26:46 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f5(_: [u32; 5]) {} + | ^^^^^^^^ this argument doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs new file mode 100644 index 00000000000..5746d14f9b1 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs @@ -0,0 +1,84 @@ +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(cmse_nonsecure_entry, no_core, lang_items)] +#![no_core] +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +impl Copy for u32 {} +impl Copy for u8 {} + +#[repr(C)] +pub struct ReprCU64(u64); + +#[repr(C)] +pub struct ReprCBytes(u8, u8, u8, u8, u8); + +#[repr(C)] +pub struct U64Compound(u32, u32); + +#[repr(C, align(16))] +pub struct ReprCAlign16(u16); + +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 { + //~^ ERROR [E0798] + ReprCU64(0) +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes { + //~^ ERROR [E0798] + ReprCBytes(0, 1, 2, 3, 4) +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound { + //~^ ERROR [E0798] + U64Compound(2, 3) +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 { + //~^ ERROR [E0798] + ReprCAlign16(4) +} + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] { + //~^ ERROR [E0798] + [0xAA; 5] +} +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 { + //~^ ERROR [E0798] + 123 +} +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 { + //~^ ERROR [E0798] + 456 +} + +#[repr(Rust)] +pub union ReprRustUnionU64 { + _unused: u64, +} + +#[repr(C)] +pub union ReprCUnionU64 { + _unused: u64, +} + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 { + //~^ ERROR [E0798] + ReprRustUnionU64 { _unused: 1 } +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 { + //~^ ERROR [E0798] + ReprCUnionU64 { _unused: 2 } +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr new file mode 100644 index 00000000000..9c885d95318 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr @@ -0,0 +1,84 @@ +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/return-via-stack.rs:25:48 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 { + | ^^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/return-via-stack.rs:30:48 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes { + | ^^^^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/return-via-stack.rs:35:48 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound { + | ^^^^^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/return-via-stack.rs:40:48 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 { + | ^^^^^^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/return-via-stack.rs:47:48 + | +LL | pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] { + | ^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/return-via-stack.rs:53:50 + | +LL | pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 { + | ^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/return-via-stack.rs:59:50 + | +LL | pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 { + | ^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/return-via-stack.rs:76:56 + | +LL | pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 { + | ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers + --> $DIR/return-via-stack.rs:81:53 + | +LL | pub extern "C-cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 { + | ^^^^^^^^^^^^^ this type doesn't fit in the available registers + | + = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr new file mode 100644 index 00000000000..77379f7049d --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr @@ -0,0 +1,9 @@ +error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target + --> $DIR/trustzone-only.rs:5:1 + | +LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs new file mode 100644 index 00000000000..8978b35d356 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs @@ -0,0 +1,89 @@ +//@ build-pass +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(cmse_nonsecure_entry, no_core, lang_items)] +#![no_core] +#![crate_type = "lib"] +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +trait Copy {} +impl Copy for u32 {} +impl Copy for u8 {} + +#[repr(transparent)] +pub struct ReprTransparentStruct<T> { + _marker1: (), + _marker2: (), + field: T, + _marker3: (), +} + +#[repr(transparent)] +pub enum ReprTransparentEnumU64 { + A(u64), +} + +#[repr(C)] +pub struct U32Compound(u16, u16); + +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn inputs1() {} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn inputs2(_: u32, _: u32, _: u32, _: u32) {} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn inputs3(_: u64, _: u64) {} +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C-cmse-nonsecure-entry" fn inputs4(_: u128) {} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn inputs5(_: f64, _: f32, _: f32) {} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn inputs6(_: ReprTransparentStruct<u64>, _: U32Compound) {} +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C-cmse-nonsecure-entry" fn inputs7(_: [u32; 4]) {} + +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn outputs1() -> u32 { + 0 +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn outputs2() -> u64 { + 0 +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn outputs3() -> i64 { + 0 +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn outputs4() -> f64 { + 0.0 +} +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C-cmse-nonsecure-entry" fn outputs5() -> [u8; 4] { + [0xAA; 4] +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn outputs6() -> ReprTransparentStruct<u64> { + ReprTransparentStruct { _marker1: (), _marker2: (), field: 0xAA, _marker3: () } +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn outputs7( +) -> ReprTransparentStruct<ReprTransparentStruct<u64>> { + ReprTransparentStruct { + _marker1: (), + _marker2: (), + field: ReprTransparentStruct { _marker1: (), _marker2: (), field: 0xAA, _marker3: () }, + _marker3: (), + } +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn outputs8() -> ReprTransparentEnumU64 { + ReprTransparentEnumU64::A(0) +} +#[no_mangle] +pub extern "C-cmse-nonsecure-entry" fn outputs9() -> U32Compound { + U32Compound(1, 2) +} diff --git a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs index 4bea3ad87f5..c1d3321f840 100644 --- a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs +++ b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs @@ -14,10 +14,8 @@ fn f<T>( 1 }], ) -> impl Iterator<Item = SubAssign> { -//~^ ERROR the type parameter `Rhs` must be explicitly specified +//~^ ERROR expected a type, found a trait //~| ERROR `()` is not an iterator -//~| ERROR trait objects must include the `dyn` keyword -//~| ERROR the type parameter `Rhs` must be explicitly specified [E0393] } pub fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr index 416a9381124..5c4d643a28e 100644 --- a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr +++ b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr @@ -16,37 +16,6 @@ help: you might be missing a type parameter LL | fn f<T, F>( | +++ -error[E0393]: the type parameter `Rhs` must be explicitly specified - --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:27 - | -LL | ) -> impl Iterator<Item = SubAssign> { - | ^^^^^^^^^ - --> $SRC_DIR/core/src/ops/arith.rs:LL:COL - | - = note: type parameter `Rhs` must be specified for this - | - = note: because of the default `Self` reference, type parameters must be specified on object types -help: set the type parameter to the desired type - | -LL | ) -> impl Iterator<Item = SubAssign<Rhs>> { - | +++++ - -error[E0393]: the type parameter `Rhs` must be explicitly specified - --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:27 - | -LL | ) -> impl Iterator<Item = SubAssign> { - | ^^^^^^^^^ - --> $SRC_DIR/core/src/ops/arith.rs:LL:COL - | - = note: type parameter `Rhs` must be specified for this - | - = note: because of the default `Self` reference, type parameters must be specified on object types - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: set the type parameter to the desired type - | -LL | ) -> impl Iterator<Item = SubAssign<Rhs>> { - | +++++ - error[E0277]: `()` is not an iterator --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:6 | @@ -55,13 +24,13 @@ LL | ) -> impl Iterator<Item = SubAssign> { | = help: the trait `Iterator` is not implemented for `()` -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:27 | LL | ) -> impl Iterator<Item = SubAssign> { | ^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | ) -> impl Iterator<Item = dyn SubAssign> { | +++ @@ -70,7 +39,7 @@ help: you might have meant to write a bound here LL | ) -> impl Iterator<Item: SubAssign> { | ~ -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0277, E0393, E0412, E0782. +Some errors have detailed explanations: E0277, E0412, E0782. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/not_wf_param_in_rpitit.rs b/tests/ui/const-generics/not_wf_param_in_rpitit.rs index eb672194340..b454562ad49 100644 --- a/tests/ui/const-generics/not_wf_param_in_rpitit.rs +++ b/tests/ui/const-generics/not_wf_param_in_rpitit.rs @@ -1,12 +1,11 @@ //@ edition:2021 -trait Trait<const N: Trait = bar> { +trait Trait<const N: dyn Trait = bar> { //~^ ERROR: cannot find value `bar` in this scope //~| ERROR: cycle detected when computing type of `Trait::N` //~| ERROR: the trait `Trait` cannot be made into an object //~| ERROR: the trait `Trait` cannot be made into an object //~| ERROR: the trait `Trait` cannot be made into an object - //~| ERROR: trait objects must include the `dyn` keyword async fn a() {} } diff --git a/tests/ui/const-generics/not_wf_param_in_rpitit.stderr b/tests/ui/const-generics/not_wf_param_in_rpitit.stderr index ade40550c73..2500409e828 100644 --- a/tests/ui/const-generics/not_wf_param_in_rpitit.stderr +++ b/tests/ui/const-generics/not_wf_param_in_rpitit.stderr @@ -1,33 +1,33 @@ error[E0425]: cannot find value `bar` in this scope - --> $DIR/not_wf_param_in_rpitit.rs:3:30 + --> $DIR/not_wf_param_in_rpitit.rs:3:34 | -LL | trait Trait<const N: Trait = bar> { - | ^^^ not found in this scope +LL | trait Trait<const N: dyn Trait = bar> { + | ^^^ not found in this scope error[E0391]: cycle detected when computing type of `Trait::N` - --> $DIR/not_wf_param_in_rpitit.rs:3:22 + --> $DIR/not_wf_param_in_rpitit.rs:3:26 | -LL | trait Trait<const N: Trait = bar> { - | ^^^^^ +LL | trait Trait<const N: dyn Trait = bar> { + | ^^^^^ | = note: ...which immediately requires computing type of `Trait::N` again note: cycle used when computing explicit predicates of trait `Trait` --> $DIR/not_wf_param_in_rpitit.rs:3:1 | -LL | trait Trait<const N: Trait = bar> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait Trait<const N: dyn Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error[E0038]: the trait `Trait` cannot be made into an object --> $DIR/not_wf_param_in_rpitit.rs:3:22 | -LL | trait Trait<const N: Trait = bar> { - | ^^^^^ `Trait` cannot be made into an object +LL | trait Trait<const N: dyn Trait = bar> { + | ^^^^^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/not_wf_param_in_rpitit.rs:10:14 + --> $DIR/not_wf_param_in_rpitit.rs:9:14 | -LL | trait Trait<const N: Trait = bar> { +LL | trait Trait<const N: dyn Trait = bar> { | ----- this trait cannot be made into an object... ... LL | async fn a() {} @@ -44,13 +44,13 @@ LL | async fn a() where Self: Sized {} error[E0038]: the trait `Trait` cannot be made into an object --> $DIR/not_wf_param_in_rpitit.rs:3:13 | -LL | trait Trait<const N: Trait = bar> { - | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object +LL | trait Trait<const N: dyn Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/not_wf_param_in_rpitit.rs:10:14 + --> $DIR/not_wf_param_in_rpitit.rs:9:14 | -LL | trait Trait<const N: Trait = bar> { +LL | trait Trait<const N: dyn Trait = bar> { | ----- this trait cannot be made into an object... ... LL | async fn a() {} @@ -67,13 +67,13 @@ LL | async fn a() where Self: Sized {} error[E0038]: the trait `Trait` cannot be made into an object --> $DIR/not_wf_param_in_rpitit.rs:3:13 | -LL | trait Trait<const N: Trait = bar> { - | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object +LL | trait Trait<const N: dyn Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/not_wf_param_in_rpitit.rs:10:14 + --> $DIR/not_wf_param_in_rpitit.rs:9:14 | -LL | trait Trait<const N: Trait = bar> { +LL | trait Trait<const N: dyn Trait = bar> { | ----- this trait cannot be made into an object... ... LL | async fn a() {} @@ -88,18 +88,7 @@ help: alternatively, consider constraining `a` so it does not apply to trait obj LL | async fn a() where Self: Sized {} | +++++++++++++++++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/not_wf_param_in_rpitit.rs:3:22 - | -LL | trait Trait<const N: Trait = bar> { - | ^^^^^ - | -help: add `dyn` keyword before this trait - | -LL | trait Trait<const N: dyn Trait = bar> { - | +++ - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0038, E0391, E0425, E0782. +Some errors have detailed explanations: E0038, E0391, E0425. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/consts/const-unwrap.rs b/tests/ui/consts/const-unwrap.rs index bc79c7db2fc..ea0a15af1be 100644 --- a/tests/ui/consts/const-unwrap.rs +++ b/tests/ui/consts/const-unwrap.rs @@ -1,11 +1,15 @@ //@ check-fail - -#![feature(const_option)] +// Verify that panicking `const_option` methods do the correct thing const FOO: i32 = Some(42i32).unwrap(); const BAR: i32 = Option::<i32>::None.unwrap(); -//~^ERROR: evaluation of constant value failed +//~^ ERROR: evaluation of constant value failed +//~| NOTE: the evaluated program panicked + +const BAZ: i32 = Option::<i32>::None.expect("absolutely not!"); +//~^ ERROR: evaluation of constant value failed +//~| NOTE: absolutely not! fn main() { println!("{}", FOO); diff --git a/tests/ui/consts/const-unwrap.stderr b/tests/ui/consts/const-unwrap.stderr index fee22a1d070..aa5dd9a5c36 100644 --- a/tests/ui/consts/const-unwrap.stderr +++ b/tests/ui/consts/const-unwrap.stderr @@ -1,9 +1,15 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const-unwrap.rs:7:18 + --> $DIR/const-unwrap.rs:6:18 | LL | const BAR: i32 = Option::<i32>::None.unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'called `Option::unwrap()` on a `None` value', $DIR/const-unwrap.rs:7:38 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'called `Option::unwrap()` on a `None` value', $DIR/const-unwrap.rs:6:38 -error: aborting due to 1 previous error +error[E0080]: evaluation of constant value failed + --> $DIR/const-unwrap.rs:10:18 + | +LL | const BAZ: i32 = Option::<i32>::None.expect("absolutely not!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'absolutely not!', $DIR/const-unwrap.rs:10:38 + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/load-preserves-partial-init.rs b/tests/ui/consts/load-preserves-partial-init.rs index d97e9cb3d9d..2a8adbd5e16 100644 --- a/tests/ui/consts/load-preserves-partial-init.rs +++ b/tests/ui/consts/load-preserves-partial-init.rs @@ -1,6 +1,5 @@ //@ run-pass -#![feature(const_ptr_write)] // issue: https://github.com/rust-lang/rust/issues/69488 // Loads of partially-initialized data could produce completely-uninitialized results. // Test to make sure that we no longer do such a "deinitializing" load. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs index 8d8917fd319..3ca58b28181 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs @@ -19,8 +19,8 @@ trait ImportantTrait3 {} trait ImportantTrait4 {} #[diagnostic::on_unimplemented(message = "Test {Self:!}")] -//~^WARN expected `'}'`, found `'!'` -//~|WARN expected `'}'`, found `'!'` +//~^WARN expected `}`, found `!` +//~|WARN expected `}`, found `!` //~|WARN unmatched `}` found //~|WARN unmatched `}` found trait ImportantTrait5 {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr index 932e81ca48e..b4ed06cb63d 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr @@ -30,7 +30,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] | = help: no format specifier are supported in this position -warning: expected `'}'`, found `'!'` +warning: expected `}`, found `!` --> $DIR/broken_format.rs:21:32 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] @@ -153,7 +153,7 @@ note: required by a bound in `check_4` LL | fn check_4(_: impl ImportantTrait4) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_4` -warning: expected `'}'`, found `'!'` +warning: expected `}`, found `!` --> $DIR/broken_format.rs:21:32 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr index 4e3d2ebebad..83795f3128e 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr @@ -1,41 +1,19 @@ -error[E0038]: the trait `Copy` cannot be made into an object +error[E0782]: expected a type, found a trait --> $DIR/avoid-ice-on-warning-2.rs:4:13 | LL | fn id<F>(f: Copy) -> usize { - | ^^^^ `Copy` cannot be made into an object + | ^^^^ | - = note: the trait cannot be made into an object because it requires `Self: Sized` - = note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - -error[E0618]: expected function, found `(dyn Copy + 'static)` - --> $DIR/avoid-ice-on-warning-2.rs:11:5 + = note: `Copy` it is dyn-incompatible, so it can't be `dyn` +help: use a new generic type parameter, constrained by `Copy` | -LL | fn id<F>(f: Copy) -> usize { - | - `f` has type `(dyn Copy + 'static)` -... -LL | f() - | ^-- - | | - | call expression requires function - -error[E0277]: the size for values of type `(dyn Copy + 'static)` cannot be known at compilation time - --> $DIR/avoid-ice-on-warning-2.rs:4:13 - | -LL | fn id<F>(f: Copy) -> usize { - | ^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `(dyn Copy + 'static)` - = help: unsized fn params are gated as an unstable feature -help: you can use `impl Trait` as the argument type +LL | fn id<F, T: Copy>(f: T) -> usize { + | +++++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn id<F>(f: impl Copy) -> usize { | ++++ -help: function arguments must have a statically known size, borrowed types always have a known size - | -LL | fn id<F>(f: &dyn Copy) -> usize { - | ++++ -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0038, E0277, E0618. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr index 180cd679dea..54daefea31c 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr @@ -36,7 +36,7 @@ LL | fn id<F>(f: Copy) -> usize { = note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> error[E0618]: expected function, found `(dyn Copy + 'static)` - --> $DIR/avoid-ice-on-warning-2.rs:11:5 + --> $DIR/avoid-ice-on-warning-2.rs:12:5 | LL | fn id<F>(f: Copy) -> usize { | - `f` has type `(dyn Copy + 'static)` diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.rs b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.rs index db2f4aea05b..3c2da667b39 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.rs +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.rs @@ -2,13 +2,14 @@ //@[old] edition:2015 //@[new] edition:2021 fn id<F>(f: Copy) -> usize { -//~^ ERROR the trait `Copy` cannot be made into an object -//~| ERROR: the size for values of type `(dyn Copy + 'static)` +//[new]~^ ERROR expected a type, found a trait +//[old]~^^ ERROR the trait `Copy` cannot be made into an object +//[old]~| ERROR the size for values of type `(dyn Copy + 'static)` //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! f() - //~^ ERROR: expected function, found `(dyn Copy + 'static)` + //[old]~^ ERROR: expected function, found `(dyn Copy + 'static)` } fn main() {} diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr index fdd3e8ab507..813b5863738 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr @@ -1,47 +1,57 @@ -error[E0038]: the trait `A` cannot be made into an object - --> $DIR/avoid-ice-on-warning-3.rs:4:19 +error[E0782]: expected a type, found a trait + --> $DIR/avoid-ice-on-warning-3.rs:14:19 | -LL | trait B { fn f(a: A) -> A; } - | ^ `A` cannot be made into an object +LL | trait A { fn g(b: B) -> B; } + | ^ | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/avoid-ice-on-warning-3.rs:12:14 + = note: `B` it is dyn-incompatible, so it can't be `dyn` +help: use a new generic type parameter, constrained by `B` + | +LL | trait A { fn g<T: B>(b: T) -> B; } + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | trait A { fn g(b: impl B) -> B; } + | ++++ + +error[E0782]: expected a type, found a trait + --> $DIR/avoid-ice-on-warning-3.rs:14:25 | LL | trait A { fn g(b: B) -> B; } - | - ^ ...because associated function `g` has no `self` parameter - | | - | this trait cannot be made into an object... -help: consider turning `g` into a method by giving it a `&self` argument + | ^ | -LL | trait A { fn g(&self, b: B) -> B; } - | ++++++ -help: alternatively, consider constraining `g` so it does not apply to trait objects +help: `B` is dyn-incompatible, use `impl B` to return an opaque type, as long as you return a single underlying type | -LL | trait A { fn g(b: B) -> B where Self: Sized; } - | +++++++++++++++++ +LL | trait A { fn g(b: B) -> impl B; } + | ++++ -error[E0038]: the trait `B` cannot be made into an object - --> $DIR/avoid-ice-on-warning-3.rs:12:19 +error[E0782]: expected a type, found a trait + --> $DIR/avoid-ice-on-warning-3.rs:4:19 | -LL | trait A { fn g(b: B) -> B; } - | ^ `B` cannot be made into an object +LL | trait B { fn f(a: A) -> A; } + | ^ | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/avoid-ice-on-warning-3.rs:4:14 + = note: `A` it is dyn-incompatible, so it can't be `dyn` +help: use a new generic type parameter, constrained by `A` + | +LL | trait B { fn f<T: A>(a: T) -> A; } + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | trait B { fn f(a: impl A) -> A; } + | ++++ + +error[E0782]: expected a type, found a trait + --> $DIR/avoid-ice-on-warning-3.rs:4:25 | LL | trait B { fn f(a: A) -> A; } - | - ^ ...because associated function `f` has no `self` parameter - | | - | this trait cannot be made into an object... -help: consider turning `f` into a method by giving it a `&self` argument + | ^ | -LL | trait B { fn f(&self, a: A) -> A; } - | ++++++ -help: alternatively, consider constraining `f` so it does not apply to trait objects +help: `A` is dyn-incompatible, use `impl A` to return an opaque type, as long as you return a single underlying type | -LL | trait B { fn f(a: A) -> A where Self: Sized; } - | +++++++++++++++++ +LL | trait B { fn f(a: A) -> impl A; } + | ++++ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr index bd362abb355..6bc2d73a0d0 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr @@ -26,7 +26,7 @@ LL | trait B { fn f(a: A) -> dyn A; } | +++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/avoid-ice-on-warning-3.rs:12:19 + --> $DIR/avoid-ice-on-warning-3.rs:14:19 | LL | trait A { fn g(b: B) -> B; } | ^ @@ -39,7 +39,7 @@ LL | trait A { fn g(b: dyn B) -> B; } | +++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/avoid-ice-on-warning-3.rs:12:25 + --> $DIR/avoid-ice-on-warning-3.rs:14:25 | LL | trait A { fn g(b: B) -> B; } | ^ @@ -72,7 +72,7 @@ LL | trait B { fn f(a: A) -> A; } | ^ `A` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/avoid-ice-on-warning-3.rs:12:14 + --> $DIR/avoid-ice-on-warning-3.rs:14:14 | LL | trait A { fn g(b: B) -> B; } | - ^ ...because associated function `g` has no `self` parameter @@ -88,7 +88,7 @@ LL | trait A { fn g(b: B) -> B where Self: Sized; } | +++++++++++++++++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/avoid-ice-on-warning-3.rs:12:19 + --> $DIR/avoid-ice-on-warning-3.rs:14:19 | LL | trait A { fn g(b: B) -> B; } | ^ @@ -102,7 +102,7 @@ LL | trait A { fn g(b: dyn B) -> B; } | +++ error[E0038]: the trait `B` cannot be made into an object - --> $DIR/avoid-ice-on-warning-3.rs:12:19 + --> $DIR/avoid-ice-on-warning-3.rs:14:19 | LL | trait A { fn g(b: B) -> B; } | ^ `B` cannot be made into an object diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.rs b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.rs index 38bee8142bb..00d47225e92 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.rs +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.rs @@ -2,7 +2,9 @@ //@[old] edition:2015 //@[new] edition:2021 trait B { fn f(a: A) -> A; } -//~^ ERROR the trait `A` cannot be made into an object +//[new]~^ ERROR expected a type, found a trait +//[new]~| ERROR expected a type, found a trait +//[old]~^^^ ERROR the trait `A` cannot be made into an object //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN trait objects without an explicit `dyn` are deprecated @@ -10,7 +12,9 @@ trait B { fn f(a: A) -> A; } //[old]~| WARN this is accepted in the current edition //[old]~| WARN this is accepted in the current edition trait A { fn g(b: B) -> B; } -//~^ ERROR the trait `B` cannot be made into an object +//[new]~^ ERROR expected a type, found a trait +//[new]~| ERROR expected a type, found a trait +//[old]~^^^ ERROR the trait `B` cannot be made into an object //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN trait objects without an explicit `dyn` are deprecated diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning.new.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning.new.stderr index 4ff45d7a848..e9eb1cdd0c2 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning.new.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning.new.stderr @@ -15,6 +15,18 @@ error[E0405]: cannot find trait `call_that` in this scope LL | fn call_this<F>(f: F) : Fn(&str) + call_that {} | ^^^^^^^^^ not found in this scope -error: aborting due to 2 previous errors +error[E0782]: expected a type, found a trait + --> $DIR/avoid-ice-on-warning.rs:4:25 + | +LL | fn call_this<F>(f: F) : Fn(&str) + call_that {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: `Fn(&str) + call_that` is dyn-incompatible, use `impl Fn(&str) + call_that` to return an opaque type, as long as you return a single underlying type + | +LL | fn call_this<F>(f: F) : impl Fn(&str) + call_that {} + | ++++ + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0405`. +Some errors have detailed explanations: E0405, E0782. +For more information about an error, try `rustc --explain E0405`. diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning.rs b/tests/ui/dyn-compatibility/avoid-ice-on-warning.rs index b90d8911d50..ef82321df0e 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning.rs +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning.rs @@ -4,6 +4,7 @@ fn call_this<F>(f: F) : Fn(&str) + call_that {} //~^ ERROR return types are denoted using `->` //~| ERROR cannot find trait `call_that` in this scope +//[new]~| ERROR expected a type, found a trait //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! fn main() {} diff --git a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.fixed b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.fixed index 4f5310082e1..a54892afd3e 100644 --- a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.fixed +++ b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.fixed @@ -4,7 +4,8 @@ //@[new] run-rustfix #![deny(bare_trait_objects)] fn ord_prefer_dot(s: String) -> impl Ord { - //~^ ERROR the trait `Ord` cannot be made into an object + //[new]~^ ERROR expected a type, found a trait + //[old]~^^ ERROR the trait `Ord` cannot be made into an object //[old]~| ERROR trait objects without an explicit `dyn` are deprecated //[old]~| WARNING this is accepted in the current edition (Rust 2015) (s.starts_with("."), s) diff --git a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.stderr b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.stderr index bb2bf6ddcda..52168261a64 100644 --- a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.stderr +++ b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.stderr @@ -1,21 +1,14 @@ -error[E0038]: the trait `Ord` cannot be made into an object +error[E0782]: expected a type, found a trait --> $DIR/bare-trait-dont-suggest-dyn.rs:6:33 | LL | fn ord_prefer_dot(s: String) -> Ord { - | ^^^ `Ord` cannot be made into an object + | ^^^ | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $SRC_DIR/core/src/cmp.rs:LL:COL - | - = note: the trait cannot be made into an object because it uses `Self` as a type parameter - ::: $SRC_DIR/core/src/cmp.rs:LL:COL - | - = note: the trait cannot be made into an object because it uses `Self` as a type parameter -help: consider using an opaque type instead +help: `Ord` is dyn-incompatible, use `impl Ord` to return an opaque type, as long as you return a single underlying type | LL | fn ord_prefer_dot(s: String) -> impl Ord { | ++++ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.rs b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.rs index cb5a305eab0..cf9be612d2e 100644 --- a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.rs +++ b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.rs @@ -4,7 +4,8 @@ //@[new] run-rustfix #![deny(bare_trait_objects)] fn ord_prefer_dot(s: String) -> Ord { - //~^ ERROR the trait `Ord` cannot be made into an object + //[new]~^ ERROR expected a type, found a trait + //[old]~^^ ERROR the trait `Ord` cannot be made into an object //[old]~| ERROR trait objects without an explicit `dyn` are deprecated //[old]~| WARNING this is accepted in the current edition (Rust 2015) (s.starts_with("."), s) diff --git a/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs b/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs index dabaa309c16..7bc2af463cb 100644 --- a/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs +++ b/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs @@ -6,104 +6,102 @@ struct IceCream; impl IceCream { fn foo(_: &Trait) {} - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait fn bar(self, _: &'a Trait) {} - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait //~| ERROR: use of undeclared lifetime name fn alice<'a>(&self, _: &Trait) {} - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait fn bob<'a>(_: &'a Trait) {} - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait fn cat() -> &Trait { //~^ ERROR: missing lifetime specifier - //~| ERROR: trait objects must include the `dyn` keyword + //~| ERROR: expected a type, found a trait &Type } fn dog<'a>() -> &Trait { //~^ ERROR: missing lifetime specifier - //~| ERROR: trait objects must include the `dyn` keyword + //~| ERROR: expected a type, found a trait &Type } fn kitten() -> &'a Trait { //~^ ERROR: use of undeclared lifetime name - //~| ERROR: trait objects must include the `dyn` keyword + //~| ERROR: expected a type, found a trait &Type } fn puppy<'a>() -> &'a Trait { - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait &Type } fn parrot() -> &mut Trait { //~^ ERROR: missing lifetime specifier - //~| ERROR: trait objects must include the `dyn` keyword + //~| ERROR: expected a type, found a trait &mut Type - //~^ ERROR: cannot return reference to temporary value } } trait Sing { fn foo(_: &Trait); - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait fn bar(_: &'a Trait); - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait //~| ERROR: use of undeclared lifetime name fn alice<'a>(_: &Trait); - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait fn bob<'a>(_: &'a Trait); - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait fn cat() -> &Trait; //~^ ERROR: missing lifetime specifier - //~| ERROR: trait objects must include the `dyn` keyword + //~| ERROR: expected a type, found a trait fn dog<'a>() -> &Trait { //~^ ERROR: missing lifetime specifier - //~| ERROR: trait objects must include the `dyn` keyword + //~| ERROR: expected a type, found a trait &Type } fn kitten() -> &'a Trait { //~^ ERROR: use of undeclared lifetime name - //~| ERROR: trait objects must include the `dyn` keyword + //~| ERROR: expected a type, found a trait &Type } fn puppy<'a>() -> &'a Trait { - //~^ ERROR: trait objects must include the `dyn` keyword + //~^ ERROR: expected a type, found a trait &Type } fn parrot() -> &mut Trait { //~^ ERROR: missing lifetime specifier - //~| ERROR: trait objects must include the `dyn` keyword + //~| ERROR: expected a type, found a trait &mut Type - //~^ ERROR: cannot return reference to temporary value } } fn foo(_: &Trait) {} -//~^ ERROR: trait objects must include the `dyn` keyword +//~^ ERROR: expected a type, found a trait fn bar(_: &'a Trait) {} -//~^ ERROR: trait objects must include the `dyn` keyword +//~^ ERROR: expected a type, found a trait //~| ERROR: use of undeclared lifetime name fn alice<'a>(_: &Trait) {} -//~^ ERROR: trait objects must include the `dyn` keyword +//~^ ERROR: expected a type, found a trait fn bob<'a>(_: &'a Trait) {} -//~^ ERROR: trait objects must include the `dyn` keyword +//~^ ERROR: expected a type, found a trait struct Type; @@ -111,32 +109,31 @@ impl Trait for Type {} fn cat() -> &Trait { //~^ ERROR: missing lifetime specifier -//~| ERROR: trait objects must include the `dyn` keyword +//~| ERROR: expected a type, found a trait &Type } fn dog<'a>() -> &Trait { //~^ ERROR: missing lifetime specifier -//~| ERROR: trait objects must include the `dyn` keyword +//~| ERROR: expected a type, found a trait &Type } fn kitten() -> &'a Trait { //~^ ERROR: use of undeclared lifetime name -//~| ERROR: trait objects must include the `dyn` keyword +//~| ERROR: expected a type, found a trait &Type } fn puppy<'a>() -> &'a Trait { -//~^ ERROR: trait objects must include the `dyn` keyword +//~^ ERROR: expected a type, found a trait &Type } fn parrot() -> &mut Trait { //~^ ERROR: missing lifetime specifier - //~| ERROR: trait objects must include the `dyn` keyword + //~| ERROR: expected a type, found a trait &mut Type - //~^ ERROR: cannot return reference to temporary value } fn main() {} diff --git a/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr b/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr index 8bdfea7766e..4c6d84f0534 100644 --- a/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr +++ b/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr @@ -65,7 +65,7 @@ LL | fn parrot() -> &'static mut Trait { | +++++++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:56:16 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:55:16 | LL | fn bar(_: &'a Trait); | ^^ undeclared lifetime @@ -80,7 +80,7 @@ LL | trait Sing<'a> { | ++++ error[E0106]: missing lifetime specifier - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:66:17 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:65:17 | LL | fn cat() -> &Trait; | ^ expected named lifetime parameter @@ -97,7 +97,7 @@ LL + fn cat() -> Trait; | error[E0106]: missing lifetime specifier - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:70:21 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:69:21 | LL | fn dog<'a>() -> &Trait { | ^ expected named lifetime parameter @@ -109,7 +109,7 @@ LL | fn dog<'a>() -> &'a Trait { | ++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:76:21 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:75:21 | LL | fn kitten() -> &'a Trait { | ^^ undeclared lifetime @@ -124,7 +124,7 @@ LL | trait Sing<'a> { | ++++ error[E0106]: missing lifetime specifier - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:87:20 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:86:20 | LL | fn parrot() -> &mut Trait { | ^ expected named lifetime parameter @@ -136,7 +136,7 @@ LL | fn parrot() -> &'static mut Trait { | +++++++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:98:12 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:96:12 | LL | fn bar(_: &'a Trait) {} | - ^^ undeclared lifetime @@ -144,7 +144,7 @@ LL | fn bar(_: &'a Trait) {} | help: consider introducing lifetime `'a` here: `<'a>` error[E0106]: missing lifetime specifier - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:112:13 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:110:13 | LL | fn cat() -> &Trait { | ^ expected named lifetime parameter @@ -156,7 +156,7 @@ LL | fn cat() -> &'static Trait { | +++++++ error[E0106]: missing lifetime specifier - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:118:17 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:116:17 | LL | fn dog<'a>() -> &Trait { | ^ expected named lifetime parameter @@ -168,7 +168,7 @@ LL | fn dog<'a>() -> &'a Trait { | ++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:124:17 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:122:17 | LL | fn kitten() -> &'a Trait { | - ^^ undeclared lifetime @@ -176,7 +176,7 @@ LL | fn kitten() -> &'a Trait { | help: consider introducing lifetime `'a` here: `<'a>` error[E0106]: missing lifetime specifier - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:135:16 + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:133:16 | LL | fn parrot() -> &mut Trait { | ^ expected named lifetime parameter @@ -187,35 +187,8 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're LL | fn parrot() -> &'static mut Trait { | +++++++ -error[E0515]: cannot return reference to temporary value - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:47:9 - | -LL | &mut Type - | ^^^^^---- - | | | - | | temporary value created here - | returns a reference to data owned by the current function - -error[E0515]: cannot return reference to temporary value - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:90:9 - | -LL | &mut Type - | ^^^^^---- - | | | - | | temporary value created here - | returns a reference to data owned by the current function - -error[E0515]: cannot return reference to temporary value - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:138:5 - | -LL | &mut Type - | ^^^^^---- - | | | - | | temporary value created here - | returns a reference to data owned by the current function - -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:53:16 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:52:16 | LL | fn foo(_: &Trait); | ^^^^^ @@ -233,8 +206,8 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn foo(_: &dyn Trait); | +++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:56:19 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:55:19 | LL | fn bar(_: &'a Trait); | ^^^^^ @@ -252,8 +225,8 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn bar(_: &'a dyn Trait); | +++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:60:22 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:59:22 | LL | fn alice<'a>(_: &Trait); | ^^^^^ @@ -271,8 +244,8 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn alice<'a>(_: &dyn Trait); | +++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:63:23 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:62:23 | LL | fn bob<'a>(_: &'a Trait); | ^^^^^ @@ -290,8 +263,8 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn bob<'a>(_: &'a dyn Trait); | +++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:66:18 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:65:18 | LL | fn cat() -> &Trait; | ^^^^^ @@ -305,8 +278,8 @@ help: alternatively, you can return an owned trait object LL | fn cat() -> Box<dyn Trait>; | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:70:22 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:69:22 | LL | fn dog<'a>() -> &Trait { | ^^^^^ @@ -320,8 +293,8 @@ help: alternatively, you can return an owned trait object LL | fn dog<'a>() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:76:24 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:75:24 | LL | fn kitten() -> &'a Trait { | ^^^^^ @@ -335,8 +308,8 @@ help: alternatively, you can return an owned trait object LL | fn kitten() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:82:27 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:81:27 | LL | fn puppy<'a>() -> &'a Trait { | ^^^^^ @@ -350,8 +323,8 @@ help: alternatively, you can return an owned trait object LL | fn puppy<'a>() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:87:25 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:86:25 | LL | fn parrot() -> &mut Trait { | ^^^^^ @@ -365,8 +338,8 @@ help: alternatively, you can return an owned trait object LL | fn parrot() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:95:12 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:93:12 | LL | fn foo(_: &Trait) {} | ^^^^^ @@ -384,8 +357,8 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn foo(_: &dyn Trait) {} | +++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:98:15 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:96:15 | LL | fn bar(_: &'a Trait) {} | ^^^^^ @@ -403,8 +376,8 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn bar(_: &'a dyn Trait) {} | +++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:102:18 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:100:18 | LL | fn alice<'a>(_: &Trait) {} | ^^^^^ @@ -422,8 +395,8 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn alice<'a>(_: &dyn Trait) {} | +++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:105:19 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:103:19 | LL | fn bob<'a>(_: &'a Trait) {} | ^^^^^ @@ -441,8 +414,8 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn bob<'a>(_: &'a dyn Trait) {} | +++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:112:14 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:110:14 | LL | fn cat() -> &Trait { | ^^^^^ @@ -456,8 +429,8 @@ help: alternatively, you can return an owned trait object LL | fn cat() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:118:18 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:116:18 | LL | fn dog<'a>() -> &Trait { | ^^^^^ @@ -471,8 +444,8 @@ help: alternatively, you can return an owned trait object LL | fn dog<'a>() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:124:20 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:122:20 | LL | fn kitten() -> &'a Trait { | ^^^^^ @@ -486,8 +459,8 @@ help: alternatively, you can return an owned trait object LL | fn kitten() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:130:23 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:128:23 | LL | fn puppy<'a>() -> &'a Trait { | ^^^^^ @@ -501,8 +474,8 @@ help: alternatively, you can return an owned trait object LL | fn puppy<'a>() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:135:21 +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:133:21 | LL | fn parrot() -> &mut Trait { | ^^^^^ @@ -516,7 +489,7 @@ help: alternatively, you can return an owned trait object LL | fn parrot() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:8:16 | LL | fn foo(_: &Trait) {} @@ -535,7 +508,7 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn foo(_: &dyn Trait) {} | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:11:25 | LL | fn bar(self, _: &'a Trait) {} @@ -554,7 +527,7 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn bar(self, _: &'a dyn Trait) {} | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:15:29 | LL | fn alice<'a>(&self, _: &Trait) {} @@ -573,7 +546,7 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn alice<'a>(&self, _: &dyn Trait) {} | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:18:23 | LL | fn bob<'a>(_: &'a Trait) {} @@ -592,7 +565,7 @@ help: alternatively, use a trait object to accept any type that implements `Trai LL | fn bob<'a>(_: &'a dyn Trait) {} | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:21:18 | LL | fn cat() -> &Trait { @@ -607,7 +580,7 @@ help: alternatively, you can return an owned trait object LL | fn cat() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:27:22 | LL | fn dog<'a>() -> &Trait { @@ -622,7 +595,7 @@ help: alternatively, you can return an owned trait object LL | fn dog<'a>() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:33:24 | LL | fn kitten() -> &'a Trait { @@ -637,7 +610,7 @@ help: alternatively, you can return an owned trait object LL | fn kitten() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:39:27 | LL | fn puppy<'a>() -> &'a Trait { @@ -652,7 +625,7 @@ help: alternatively, you can return an owned trait object LL | fn puppy<'a>() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:44:25 | LL | fn parrot() -> &mut Trait { @@ -667,7 +640,7 @@ help: alternatively, you can return an owned trait object LL | fn parrot() -> Box<dyn Trait> { | ~~~~~~~~~~~~~~ -error: aborting due to 45 previous errors +error: aborting due to 42 previous errors -Some errors have detailed explanations: E0106, E0261, E0515, E0782. +Some errors have detailed explanations: E0106, E0261, E0782. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/dyn-keyword/dyn-2021-edition-error.rs b/tests/ui/dyn-keyword/dyn-2021-edition-error.rs index f98bf4ef5d1..5d607d82ea1 100644 --- a/tests/ui/dyn-keyword/dyn-2021-edition-error.rs +++ b/tests/ui/dyn-keyword/dyn-2021-edition-error.rs @@ -1,10 +1,10 @@ //@ edition:2021 fn function(x: &SomeTrait, y: Box<SomeTrait>) { - //~^ ERROR trait objects must include the `dyn` keyword - //~| ERROR trait objects must include the `dyn` keyword + //~^ ERROR expected a type, found a trait + //~| ERROR expected a type, found a trait let _x: &SomeTrait = todo!(); - //~^ ERROR trait objects must include the `dyn` keyword + //~^ ERROR expected a type, found a trait } trait SomeTrait {} diff --git a/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr b/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr index 52ee6c81ab7..6d1a1618ac0 100644 --- a/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr +++ b/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr @@ -1,4 +1,4 @@ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/dyn-2021-edition-error.rs:3:17 | LL | fn function(x: &SomeTrait, y: Box<SomeTrait>) { @@ -17,24 +17,24 @@ help: alternatively, use a trait object to accept any type that implements `Some LL | fn function(x: &dyn SomeTrait, y: Box<SomeTrait>) { | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/dyn-2021-edition-error.rs:3:35 | LL | fn function(x: &SomeTrait, y: Box<SomeTrait>) { | ^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | fn function(x: &SomeTrait, y: Box<dyn SomeTrait>) { | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/dyn-2021-edition-error.rs:6:14 | LL | let _x: &SomeTrait = todo!(); | ^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | let _x: &dyn SomeTrait = todo!(); | +++ diff --git a/tests/ui/dyn-keyword/suggest-dyn-on-bare-trait-in-pat.rs b/tests/ui/dyn-keyword/suggest-dyn-on-bare-trait-in-pat.rs index 19b5edb620f..bee15788da9 100644 --- a/tests/ui/dyn-keyword/suggest-dyn-on-bare-trait-in-pat.rs +++ b/tests/ui/dyn-keyword/suggest-dyn-on-bare-trait-in-pat.rs @@ -9,6 +9,7 @@ impl dyn Trait { fn main() { match () { Trait::CONST => {} - //~^ ERROR trait objects must include the `dyn` keyword + //~^ ERROR expected a type, found a trait + //~| HELP you can add the `dyn` keyword if you want a trait object } } diff --git a/tests/ui/dyn-keyword/suggest-dyn-on-bare-trait-in-pat.stderr b/tests/ui/dyn-keyword/suggest-dyn-on-bare-trait-in-pat.stderr index 4446a89b63b..6a2bb3ec09a 100644 --- a/tests/ui/dyn-keyword/suggest-dyn-on-bare-trait-in-pat.stderr +++ b/tests/ui/dyn-keyword/suggest-dyn-on-bare-trait-in-pat.stderr @@ -1,10 +1,10 @@ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-dyn-on-bare-trait-in-pat.rs:11:9 | LL | Trait::CONST => {} | ^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | <dyn Trait>::CONST => {} | ++++ + diff --git a/tests/ui/editions/dyn-trait-sugg-2021.rs b/tests/ui/editions/dyn-trait-sugg-2021.rs index d702bfb2f88..a5364662600 100644 --- a/tests/ui/editions/dyn-trait-sugg-2021.rs +++ b/tests/ui/editions/dyn-trait-sugg-2021.rs @@ -8,5 +8,5 @@ impl<T> dyn Foo<T> { fn main() { Foo::hi(123); - //~^ ERROR trait objects must include the `dyn` keyword + //~^ ERROR expected a type, found a trait } diff --git a/tests/ui/editions/dyn-trait-sugg-2021.stderr b/tests/ui/editions/dyn-trait-sugg-2021.stderr index 8a65fea11c9..3aea8ac491d 100644 --- a/tests/ui/editions/dyn-trait-sugg-2021.stderr +++ b/tests/ui/editions/dyn-trait-sugg-2021.stderr @@ -1,10 +1,10 @@ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/dyn-trait-sugg-2021.rs:10:5 | LL | Foo::hi(123); | ^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | <dyn Foo>::hi(123); | ++++ + diff --git a/tests/ui/extern/issue-95829.rs b/tests/ui/extern/issue-95829.rs index ad4e04f7c3a..c5ae4c68265 100644 --- a/tests/ui/extern/issue-95829.rs +++ b/tests/ui/extern/issue-95829.rs @@ -2,7 +2,7 @@ extern { async fn L() { //~ ERROR: incorrect function inside `extern` block - //~^ ERROR: functions in `extern` blocks cannot have qualifiers + //~^ ERROR: functions in `extern` blocks cannot have `async` qualifier async fn M() {} } } diff --git a/tests/ui/extern/issue-95829.stderr b/tests/ui/extern/issue-95829.stderr index 16504d1f0c9..2f396b8cc04 100644 --- a/tests/ui/extern/issue-95829.stderr +++ b/tests/ui/extern/issue-95829.stderr @@ -15,13 +15,13 @@ LL | | } = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html -error: functions in `extern` blocks cannot have qualifiers +error: functions in `extern` blocks cannot have `async` qualifier --> $DIR/issue-95829.rs:4:5 | LL | extern { | ------ in this `extern` block LL | async fn L() { - | ^^^^^ help: remove this qualifier + | ^^^^^ help: remove the `async` qualifier error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.has_support.stderr b/tests/ui/feature-gates/feature-gate-autodiff-use.has_support.stderr new file mode 100644 index 00000000000..36a017dd53c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.has_support.stderr @@ -0,0 +1,23 @@ +error[E0658]: use of unstable library feature 'autodiff' + --> $DIR/feature-gate-autodiff-use.rs:13:3 + | +LL | #[autodiff(dfoo, Reverse)] + | ^^^^^^^^ + | + = note: see issue #124509 <https://github.com/rust-lang/rust/issues/124509> for more information + = help: add `#![feature(autodiff)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature 'autodiff' + --> $DIR/feature-gate-autodiff-use.rs:9:5 + | +LL | use std::autodiff::autodiff; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #124509 <https://github.com/rust-lang/rust/issues/124509> for more information + = help: add `#![feature(autodiff)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.no_support.stderr b/tests/ui/feature-gates/feature-gate-autodiff-use.no_support.stderr new file mode 100644 index 00000000000..4b767f824c8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.no_support.stderr @@ -0,0 +1,29 @@ +error[E0658]: use of unstable library feature 'autodiff' + --> $DIR/feature-gate-autodiff-use.rs:13:3 + | +LL | #[autodiff(dfoo, Reverse)] + | ^^^^^^^^ + | + = note: see issue #124509 <https://github.com/rust-lang/rust/issues/124509> for more information + = help: add `#![feature(autodiff)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: this rustc version does not support autodiff + --> $DIR/feature-gate-autodiff-use.rs:13:1 + | +LL | #[autodiff(dfoo, Reverse)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0658]: use of unstable library feature 'autodiff' + --> $DIR/feature-gate-autodiff-use.rs:9:5 + | +LL | use std::autodiff::autodiff; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #124509 <https://github.com/rust-lang/rust/issues/124509> for more information + = help: add `#![feature(autodiff)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.rs b/tests/ui/feature-gates/feature-gate-autodiff-use.rs new file mode 100644 index 00000000000..2276a79d6e2 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.rs @@ -0,0 +1,17 @@ +//@ revisions: has_support no_support +//@[no_support] ignore-enzyme +//@[has_support] needs-enzyme + +// This checks that without enabling the autodiff feature, we can't import std::autodiff::autodiff; + +#![crate_type = "lib"] + +use std::autodiff::autodiff; +//[has_support]~^ ERROR use of unstable library feature 'autodiff' +//[no_support]~^^ ERROR use of unstable library feature 'autodiff' + +#[autodiff(dfoo, Reverse)] +//[has_support]~^ ERROR use of unstable library feature 'autodiff' [E0658] +//[no_support]~^^ ERROR use of unstable library feature 'autodiff' [E0658] +//[no_support]~| ERROR this rustc version does not support autodiff +fn foo() {} diff --git a/tests/ui/feature-gates/feature-gate-autodiff.has_support.stderr b/tests/ui/feature-gates/feature-gate-autodiff.has_support.stderr new file mode 100644 index 00000000000..c25cf7d3373 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-autodiff.has_support.stderr @@ -0,0 +1,13 @@ +error: cannot find attribute `autodiff` in this scope + --> $DIR/feature-gate-autodiff.rs:9:3 + | +LL | #[autodiff(dfoo, Reverse)] + | ^^^^^^^^ + | +help: consider importing this attribute macro + | +LL + use std::autodiff::autodiff; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/feature-gates/feature-gate-autodiff.no_support.stderr b/tests/ui/feature-gates/feature-gate-autodiff.no_support.stderr new file mode 100644 index 00000000000..c25cf7d3373 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-autodiff.no_support.stderr @@ -0,0 +1,13 @@ +error: cannot find attribute `autodiff` in this scope + --> $DIR/feature-gate-autodiff.rs:9:3 + | +LL | #[autodiff(dfoo, Reverse)] + | ^^^^^^^^ + | +help: consider importing this attribute macro + | +LL + use std::autodiff::autodiff; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/feature-gates/feature-gate-autodiff.rs b/tests/ui/feature-gates/feature-gate-autodiff.rs new file mode 100644 index 00000000000..4249b229a69 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-autodiff.rs @@ -0,0 +1,12 @@ +//@ revisions: has_support no_support +//@[no_support] ignore-enzyme +//@[has_support] needs-enzyme + +#![crate_type = "lib"] + +// This checks that without the autodiff feature enabled, we can't use it. + +#[autodiff(dfoo, Reverse)] +//[has_support]~^ ERROR cannot find attribute `autodiff` in this scope +//[no_support]~^^ ERROR cannot find attribute `autodiff` in this scope +fn foo() {} diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index 3382504af9d..4624faf1e53 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, incomplete_features)] +#![allow(dead_code)] use std::pin::Pin; @@ -9,10 +9,13 @@ impl Foo { } } -fn foo(_: Pin<&mut Foo>) { +fn foo(x: Pin<&mut Foo>) { + let _y: &pin mut Foo = x; //~ ERROR pinned reference syntax is experimental } -fn bar(mut x: Pin<&mut Foo>) { +fn foo_sugar(_: &pin mut Foo) {} //~ ERROR pinned reference syntax is experimental + +fn bar(x: Pin<&mut Foo>) { foo(x); foo(x); //~ ERROR use of moved value: `x` } @@ -22,4 +25,6 @@ fn baz(mut x: Pin<&mut Foo>) { x.foo(); //~ ERROR use of moved value: `x` } +fn baz_sugar(_: &pin const Foo) {} //~ ERROR pinned reference syntax is experimental + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr index 430b7866241..dd93a7be1ad 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr @@ -1,8 +1,38 @@ +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:13:14 + | +LL | let _y: &pin mut Foo = x; + | ^^^ + | + = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:16:18 + | +LL | fn foo_sugar(_: &pin mut Foo) {} + | ^^^ + | + = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:28:18 + | +LL | fn baz_sugar(_: &pin const Foo) {} + | ^^^ + | + = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0382]: use of moved value: `x` - --> $DIR/feature-gate-pin_ergonomics.rs:17:9 + --> $DIR/feature-gate-pin_ergonomics.rs:20:9 | -LL | fn bar(mut x: Pin<&mut Foo>) { - | ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait +LL | fn bar(x: Pin<&mut Foo>) { + | - move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait LL | foo(x); | - value moved here LL | foo(x); @@ -11,13 +41,13 @@ LL | foo(x); note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary --> $DIR/feature-gate-pin_ergonomics.rs:12:11 | -LL | fn foo(_: Pin<&mut Foo>) { +LL | fn foo(x: Pin<&mut Foo>) { | --- ^^^^^^^^^^^^^ this parameter takes ownership of the value | | | in this function error[E0382]: use of moved value: `x` - --> $DIR/feature-gate-pin_ergonomics.rs:22:5 + --> $DIR/feature-gate-pin_ergonomics.rs:25:5 | LL | fn baz(mut x: Pin<&mut Foo>) { | ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait @@ -36,6 +66,7 @@ help: consider reborrowing the `Pin` instead of moving it LL | x.as_mut().foo(); | +++++++++ -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0382`. +Some errors have detailed explanations: E0382, E0658. +For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.rs b/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.rs new file mode 100644 index 00000000000..9e697aaa0dc --- /dev/null +++ b/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.rs @@ -0,0 +1,10 @@ +//! Ensure #[unstable] doesn't accept already stable features + +#![feature(staged_api)] +#![stable(feature = "rust_test", since = "1.0.0")] + +#[unstable(feature = "arbitrary_enum_discriminant", issue = "42")] //~ ERROR can't mark as unstable using an already stable feature +#[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")] //~ ERROR can't mark as unstable using an already stable feature +const fn my_fun() {} + +fn main() {} diff --git a/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.stderr b/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.stderr new file mode 100644 index 00000000000..319056a9c88 --- /dev/null +++ b/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.stderr @@ -0,0 +1,31 @@ +error: can't mark as unstable using an already stable feature + --> $DIR/unstable-attribute-rejects-already-stable-features.rs:6:1 + | +LL | #[unstable(feature = "arbitrary_enum_discriminant", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this feature is already stable +LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")] +LL | const fn my_fun() {} + | -------------------- the stability attribute annotates this item + | +help: consider removing the attribute + --> $DIR/unstable-attribute-rejects-already-stable-features.rs:6:1 + | +LL | #[unstable(feature = "arbitrary_enum_discriminant", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: can't mark as unstable using an already stable feature + --> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1 + | +LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this feature is already stable +LL | const fn my_fun() {} + | -------------------- the stability attribute annotates this item + | +help: consider removing the attribute + --> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1 + | +LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/fmt/closing-brace-as-fill.rs b/tests/ui/fmt/closing-brace-as-fill.rs index 6ad257f943e..5865ee31c43 100644 --- a/tests/ui/fmt/closing-brace-as-fill.rs +++ b/tests/ui/fmt/closing-brace-as-fill.rs @@ -4,5 +4,5 @@ fn main() { println!("Hello, world! {0:}<3", 2); - //~^ ERROR invalid format string: expected `'}'` but string was terminated + //~^ ERROR invalid format string: expected `}` but string was terminated } diff --git a/tests/ui/fmt/closing-brace-as-fill.stderr b/tests/ui/fmt/closing-brace-as-fill.stderr index 70068fa3aad..aa22beddf45 100644 --- a/tests/ui/fmt/closing-brace-as-fill.stderr +++ b/tests/ui/fmt/closing-brace-as-fill.stderr @@ -1,12 +1,12 @@ -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/closing-brace-as-fill.rs:6:35 | LL | println!("Hello, world! {0:}<3", 2); - | - ^ expected `'}'` in format string + | - ^ expected `}` in format string | | | this is not interpreted as a formatting closing brace | - = note: the character `'}'` is interpreted as a fill character because of the `:` that precedes it + = note: the character `}` is interpreted as a fill character because of the `:` that precedes it error: aborting due to 1 previous error diff --git a/tests/ui/fmt/format-string-error-2.rs b/tests/ui/fmt/format-string-error-2.rs index 1f7f0d8f6be..357dd7b10a3 100644 --- a/tests/ui/fmt/format-string-error-2.rs +++ b/tests/ui/fmt/format-string-error-2.rs @@ -72,7 +72,7 @@ raw { \n // note: `\x7B` is `{` println!("\x7B}\u{8} {", 1); - //~^ ERROR invalid format string: expected `'}'` but string was terminated + //~^ ERROR invalid format string: expected `}` but string was terminated println!("\x7B}\u8 {", 1); //~^ ERROR incorrect unicode escape sequence diff --git a/tests/ui/fmt/format-string-error-2.stderr b/tests/ui/fmt/format-string-error-2.stderr index d5fe4081ac8..a2d142e0bab 100644 --- a/tests/ui/fmt/format-string-error-2.stderr +++ b/tests/ui/fmt/format-string-error-2.stderr @@ -9,138 +9,138 @@ help: format of unicode escape sequences uses braces LL | println!("\x7B}\u{8} {", 1); | ~~~~~ -error: invalid format string: expected `'}'`, found `'a'` +error: invalid format string: expected `}`, found `a` --> $DIR/format-string-error-2.rs:5:5 | LL | format!("{ | - because of this opening brace LL | a"); - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'b'` +error: invalid format string: expected `}`, found `b` --> $DIR/format-string-error-2.rs:9:5 | LL | format!("{ \ | - because of this opening brace LL | \ LL | b"); - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'\'` +error: invalid format string: expected `}`, found `\` --> $DIR/format-string-error-2.rs:11:18 | LL | format!(r#"{ \ - | - ^ expected `'}'` in format string + | - ^ expected `}` in format string | | | because of this opening brace | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'\'` +error: invalid format string: expected `}`, found `\` --> $DIR/format-string-error-2.rs:15:18 | LL | format!(r#"{ \n - | - ^ expected `'}'` in format string + | - ^ expected `}` in format string | | | because of this opening brace | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'e'` +error: invalid format string: expected `}`, found `e` --> $DIR/format-string-error-2.rs:21:5 | LL | format!("{ \n | - because of this opening brace LL | \n LL | e"); - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'a'` +error: invalid format string: expected `}`, found `a` --> $DIR/format-string-error-2.rs:25:5 | LL | { | - because of this opening brace LL | a"); - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'a'` +error: invalid format string: expected `}`, found `a` --> $DIR/format-string-error-2.rs:29:5 | LL | { | - because of this opening brace LL | a - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'b'` +error: invalid format string: expected `}`, found `b` --> $DIR/format-string-error-2.rs:35:5 | LL | { \ | - because of this opening brace LL | \ LL | b"); - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'b'` +error: invalid format string: expected `}`, found `b` --> $DIR/format-string-error-2.rs:40:5 | LL | { \ | - because of this opening brace LL | \ LL | b \ - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'\'` +error: invalid format string: expected `}`, found `\` --> $DIR/format-string-error-2.rs:45:8 | LL | raw { \ - | - ^ expected `'}'` in format string + | - ^ expected `}` in format string | | | because of this opening brace | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'\'` +error: invalid format string: expected `}`, found `\` --> $DIR/format-string-error-2.rs:50:8 | LL | raw { \n - | - ^ expected `'}'` in format string + | - ^ expected `}` in format string | | | because of this opening brace | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'e'` +error: invalid format string: expected `}`, found `e` --> $DIR/format-string-error-2.rs:57:5 | LL | { \n | - because of this opening brace LL | \n LL | e"); - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'a'` +error: invalid format string: expected `}`, found `a` --> $DIR/format-string-error-2.rs:67:5 | LL | { | - because of this opening brace LL | asdf} - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` @@ -150,11 +150,11 @@ error: 1 positional argument in format string, but no arguments were given LL | println!("\t{}"); | ^^ -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/format-string-error-2.rs:74:27 | LL | println!("\x7B}\u{8} {", 1); - | -^ expected `'}'` in format string + | -^ expected `}` in format string | | | because of this opening brace | diff --git a/tests/ui/fmt/format-string-error.rs b/tests/ui/fmt/format-string-error.rs index 9b436e2c479..cb9449be532 100644 --- a/tests/ui/fmt/format-string-error.rs +++ b/tests/ui/fmt/format-string-error.rs @@ -2,7 +2,7 @@ fn main() { println!("{"); - //~^ ERROR invalid format string: expected `'}'` but string was terminated + //~^ ERROR invalid format string: expected `}` but string was terminated println!("{{}}"); println!("}"); //~^ ERROR invalid format string: unmatched `}` found @@ -13,11 +13,11 @@ fn main() { let _ = format!("{a:._$}", a = "", _ = 0); //~^ ERROR invalid format string: invalid argument name `_` let _ = format!("{"); - //~^ ERROR invalid format string: expected `'}'` but string was terminated + //~^ ERROR invalid format string: expected `}` but string was terminated let _ = format!("}"); //~^ ERROR invalid format string: unmatched `}` found let _ = format!("{\\}"); - //~^ ERROR invalid format string: expected `'}'`, found `'\'` + //~^ ERROR invalid format string: expected `}`, found `\` let _ = format!("\n\n\n{\n\n\n"); //~^ ERROR invalid format string let _ = format!(r###" diff --git a/tests/ui/fmt/format-string-error.stderr b/tests/ui/fmt/format-string-error.stderr index 37a181e6fcb..9d6a91413d0 100644 --- a/tests/ui/fmt/format-string-error.stderr +++ b/tests/ui/fmt/format-string-error.stderr @@ -1,8 +1,8 @@ -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/format-string-error.rs:4:16 | LL | println!("{"); - | -^ expected `'}'` in format string + | -^ expected `}` in format string | | | because of this opening brace | @@ -40,11 +40,11 @@ LL | let _ = format!("{a:._$}", a = "", _ = 0); | = note: argument name cannot be a single underscore -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/format-string-error.rs:15:23 | LL | let _ = format!("{"); - | -^ expected `'}'` in format string + | -^ expected `}` in format string | | | because of this opening brace | @@ -58,44 +58,44 @@ LL | let _ = format!("}"); | = note: if you intended to print `}`, you can escape it using `}}` -error: invalid format string: expected `'}'`, found `'\'` +error: invalid format string: expected `}`, found `\` --> $DIR/format-string-error.rs:19:23 | LL | let _ = format!("{\}"); - | -^ expected `'}'` in format string + | -^ expected `}` in format string | | | because of this opening brace | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/format-string-error.rs:21:35 | LL | let _ = format!("\n\n\n{\n\n\n"); - | - ^ expected `'}'` in format string + | - ^ expected `}` in format string | | | because of this opening brace | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/format-string-error.rs:27:3 | LL | {"###); - | -^ expected `'}'` in format string + | -^ expected `}` in format string | | | because of this opening brace | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/format-string-error.rs:35:1 | LL | { | - because of this opening brace LL | LL | "###); - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` diff --git a/tests/ui/fmt/format-string-wrong-order.rs b/tests/ui/fmt/format-string-wrong-order.rs index da775be3ffd..891279b97e4 100644 --- a/tests/ui/fmt/format-string-wrong-order.rs +++ b/tests/ui/fmt/format-string-wrong-order.rs @@ -7,9 +7,9 @@ fn main() { format!("{?:?}", bar); //~^ ERROR invalid format string: expected format parameter to occur after `:` format!("{??}", bar); - //~^ ERROR invalid format string: expected `'}'`, found `'?'` + //~^ ERROR invalid format string: expected `}`, found `?` format!("{?;bar}"); - //~^ ERROR invalid format string: expected `'}'`, found `'?'` + //~^ ERROR invalid format string: expected `}`, found `?` format!("{?:#?}", bar); //~^ ERROR invalid format string: expected format parameter to occur after `:` format!("Hello {<5:}!", "x"); diff --git a/tests/ui/fmt/format-string-wrong-order.stderr b/tests/ui/fmt/format-string-wrong-order.stderr index 3ef07720c15..7f017511761 100644 --- a/tests/ui/fmt/format-string-wrong-order.stderr +++ b/tests/ui/fmt/format-string-wrong-order.stderr @@ -22,21 +22,21 @@ LL | format!("{?:?}", bar); | = note: `?` comes after `:`, try `:?` instead -error: invalid format string: expected `'}'`, found `'?'` +error: invalid format string: expected `}`, found `?` --> $DIR/format-string-wrong-order.rs:9:15 | LL | format!("{??}", bar); - | -^ expected `'}'` in format string + | -^ expected `}` in format string | | | because of this opening brace | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'`, found `'?'` +error: invalid format string: expected `}`, found `?` --> $DIR/format-string-wrong-order.rs:11:15 | LL | format!("{?;bar}"); - | -^ expected `'}'` in format string + | -^ expected `}` in format string | | | because of this opening brace | diff --git a/tests/ui/fmt/ifmt-bad-arg.rs b/tests/ui/fmt/ifmt-bad-arg.rs index 68861d7bf3f..e39ffb8fe1b 100644 --- a/tests/ui/fmt/ifmt-bad-arg.rs +++ b/tests/ui/fmt/ifmt-bad-arg.rs @@ -48,7 +48,7 @@ fn main() { // bad syntax of the format string - format!("{"); //~ ERROR: expected `'}'` but string was terminated + format!("{"); //~ ERROR: expected `}` but string was terminated format!("foo } bar"); //~ ERROR: unmatched `}` found format!("foo }"); //~ ERROR: unmatched `}` found diff --git a/tests/ui/fmt/ifmt-bad-arg.stderr b/tests/ui/fmt/ifmt-bad-arg.stderr index 09ce3dca411..4344aee83c2 100644 --- a/tests/ui/fmt/ifmt-bad-arg.stderr +++ b/tests/ui/fmt/ifmt-bad-arg.stderr @@ -136,11 +136,11 @@ LL | format!("{valuea} {valueb}", valuea=5, valuec=7); | | | formatting specifier missing -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/ifmt-bad-arg.rs:51:15 | LL | format!("{"); - | -^ expected `'}'` in format string + | -^ expected `}` in format string | | | because of this opening brace | @@ -172,13 +172,13 @@ LL | format!("foo %s baz", "bar"); | = note: printf formatting is not supported; see the documentation for `std::fmt` -error: invalid format string: expected `'}'`, found `'t'` +error: invalid format string: expected `}`, found `t` --> $DIR/ifmt-bad-arg.rs:75:1 | LL | ninth number: { | - because of this opening brace LL | tenth number: {}", - | ^ expected `'}'` in format string + | ^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` diff --git a/tests/ui/fmt/issue-91556.rs b/tests/ui/fmt/issue-91556.rs index e782e6f9076..fcf8909859c 100644 --- a/tests/ui/fmt/issue-91556.rs +++ b/tests/ui/fmt/issue-91556.rs @@ -1,8 +1,8 @@ fn main() { let _ = format!(concat!("{0}𝖳𝖾𝗌𝗍{"), i); - //~^ ERROR: invalid format string: expected `'}'` but string was terminated + //~^ ERROR: invalid format string: expected `}` but string was terminated //~| NOTE: if you intended to print `{`, you can escape it using `{{` //~| NOTE: in this expansion of concat! //~| NOTE: in this expansion of concat! - //~| NOTE: expected `'}'` in format string + //~| NOTE: expected `}` in format string } diff --git a/tests/ui/fmt/issue-91556.stderr b/tests/ui/fmt/issue-91556.stderr index beab3db0d94..52917fb8c42 100644 --- a/tests/ui/fmt/issue-91556.stderr +++ b/tests/ui/fmt/issue-91556.stderr @@ -1,8 +1,8 @@ -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/issue-91556.rs:2:19 | LL | let _ = format!(concat!("{0}𝖳𝖾𝗌𝗍{"), i); - | ^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string + | ^^^^^^^^^^^^^^^^^^^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/fmt/respanned-literal-issue-106191.rs b/tests/ui/fmt/respanned-literal-issue-106191.rs index b0c0855a870..0a127b1a0ca 100644 --- a/tests/ui/fmt/respanned-literal-issue-106191.rs +++ b/tests/ui/fmt/respanned-literal-issue-106191.rs @@ -4,7 +4,7 @@ extern crate format_string_proc_macro; fn main() { format_string_proc_macro::respan_to_invalid_format_literal!("¡"); - //~^ ERROR invalid format string: expected `'}'` but string was terminated + //~^ ERROR invalid format string: expected `}` but string was terminated format_args!(r#concat!("¡ {")); - //~^ ERROR invalid format string: expected `'}'` but string was terminated + //~^ ERROR invalid format string: expected `}` but string was terminated } diff --git a/tests/ui/fmt/respanned-literal-issue-106191.stderr b/tests/ui/fmt/respanned-literal-issue-106191.stderr index 73a3af65a38..17ab29e799b 100644 --- a/tests/ui/fmt/respanned-literal-issue-106191.stderr +++ b/tests/ui/fmt/respanned-literal-issue-106191.stderr @@ -1,16 +1,16 @@ -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/respanned-literal-issue-106191.rs:6:65 | LL | format_string_proc_macro::respan_to_invalid_format_literal!("¡"); - | ^^^ expected `'}'` in format string + | ^^^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/respanned-literal-issue-106191.rs:8:18 | LL | format_args!(r#concat!("¡ {")); - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/hygiene/panic-location.rs b/tests/ui/hygiene/panic-location.rs index 7b20f1683a9..580a8bcff05 100644 --- a/tests/ui/hygiene/panic-location.rs +++ b/tests/ui/hygiene/panic-location.rs @@ -4,8 +4,7 @@ //@ normalize-stderr-test: ".rs:\d+:\d+" -> ".rs:LL:CC" // // Regression test for issue #70963 -// The captured stderr from this test reports a location -// inside `VecDeque::with_capacity`, instead of `<::core::macros::panic macros>` +// The reported panic location should not be `<::core::macros::panic macros>`. fn main() { std::collections::VecDeque::<String>::with_capacity(!0); } diff --git a/tests/ui/hygiene/panic-location.run.stderr b/tests/ui/hygiene/panic-location.run.stderr index bfed4cd6650..b9086ecef81 100644 --- a/tests/ui/hygiene/panic-location.run.stderr +++ b/tests/ui/hygiene/panic-location.run.stderr @@ -1,3 +1,3 @@ -thread 'main' panicked at alloc/src/raw_vec.rs:LL:CC: +thread 'main' panicked at $DIR/panic-location.rs:LL:CC: capacity overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr index 7917fa991ee..1e268bc7f49 100644 --- a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr +++ b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr @@ -1,21 +1,14 @@ -error[E0277]: the trait bound `(): AsRef<(dyn for<'a> Fn(&'a ()) + 'static)>` is not satisfied - --> $DIR/generic-with-implicit-hrtb-without-dyn.rs:6:13 - | -LL | fn ice() -> impl AsRef<Fn(&())> { - | ^^^^^^^^^^^^^^^^^^^ the trait `AsRef<(dyn for<'a> Fn(&'a ()) + 'static)>` is not implemented for `()` - -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/generic-with-implicit-hrtb-without-dyn.rs:6:24 | LL | fn ice() -> impl AsRef<Fn(&())> { | ^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | fn ice() -> impl AsRef<dyn Fn(&())> { | +++ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0277, E0782. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs index 582386aa759..c71f794d5d1 100644 --- a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs +++ b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs @@ -5,8 +5,7 @@ fn ice() -> impl AsRef<Fn(&())> { //[edition2015]~^ ERROR: the trait bound `(): AsRef<(dyn for<'a> Fn(&'a ()) + 'static)>` is not satisfied [E0277] - //[edition2021]~^^ ERROR: trait objects must include the `dyn` keyword [E0782] - //[edition2021]~| ERROR: the trait bound `(): AsRef<(dyn for<'a> Fn(&'a ()) + 'static)>` is not satisfied [E0277] + //[edition2021]~^^ ERROR: expected a type, found a trait [E0782] todo!() } diff --git a/tests/ui/lint/non-local-defs/convoluted-locals-131474-with-mods.rs b/tests/ui/lint/non-local-defs/convoluted-locals-131474-with-mods.rs index cef0f0205e8..72fd056d461 100644 --- a/tests/ui/lint/non-local-defs/convoluted-locals-131474-with-mods.rs +++ b/tests/ui/lint/non-local-defs/convoluted-locals-131474-with-mods.rs @@ -31,4 +31,15 @@ const _: () = { }; }; +// https://github.com/rust-lang/rust/issues/131643 +const _: () = { + const _: () = { + impl tmp::InnerTest {} + }; + + mod tmp { + pub(super) struct InnerTest; + } +}; + fn main() {} diff --git a/tests/ui/lint/non-local-defs/convoluted-locals-131474.rs b/tests/ui/lint/non-local-defs/convoluted-locals-131474.rs index 4881723f13b..8e738544a71 100644 --- a/tests/ui/lint/non-local-defs/convoluted-locals-131474.rs +++ b/tests/ui/lint/non-local-defs/convoluted-locals-131474.rs @@ -21,4 +21,13 @@ const _: () = { }; }; +// https://github.com/rust-lang/rust/issues/131643 +const _: () = { + const _: () = { + impl InnerTest {} + }; + + struct InnerTest; +}; + fn main() {} diff --git a/tests/ui/macros/format-empty-block-unit-tuple-suggestion-130170.fixed b/tests/ui/macros/format-empty-block-unit-tuple-suggestion-130170.fixed new file mode 100644 index 00000000000..1ca5125fe8b --- /dev/null +++ b/tests/ui/macros/format-empty-block-unit-tuple-suggestion-130170.fixed @@ -0,0 +1,13 @@ +//@ run-rustfix + +fn main() { + let s = "123"; + println!("{:?} {} {}", {}, "sss", s); + //~^ ERROR format argument must be a string literal + println!("{:?}", {}); + //~^ ERROR format argument must be a string literal + println!("{} {} {} {:?}", s, "sss", s, {}); + //~^ ERROR format argument must be a string literal + println!("{:?} {} {:?}", (), s, {}); + //~^ ERROR format argument must be a string literal +} diff --git a/tests/ui/macros/format-empty-block-unit-tuple-suggestion-130170.rs b/tests/ui/macros/format-empty-block-unit-tuple-suggestion-130170.rs new file mode 100644 index 00000000000..c09b2a04061 --- /dev/null +++ b/tests/ui/macros/format-empty-block-unit-tuple-suggestion-130170.rs @@ -0,0 +1,13 @@ +//@ run-rustfix + +fn main() { + let s = "123"; + println!({}, "sss", s); + //~^ ERROR format argument must be a string literal + println!({}); + //~^ ERROR format argument must be a string literal + println!(s, "sss", s, {}); + //~^ ERROR format argument must be a string literal + println!((), s, {}); + //~^ ERROR format argument must be a string literal +} diff --git a/tests/ui/macros/format-empty-block-unit-tuple-suggestion-130170.stderr b/tests/ui/macros/format-empty-block-unit-tuple-suggestion-130170.stderr new file mode 100644 index 00000000000..81fca8c03cc --- /dev/null +++ b/tests/ui/macros/format-empty-block-unit-tuple-suggestion-130170.stderr @@ -0,0 +1,46 @@ +error: format argument must be a string literal + --> $DIR/format-empty-block-unit-tuple-suggestion-130170.rs:5:14 + | +LL | println!({}, "sss", s); + | ^^ + | +help: you might be missing a string literal to format with + | +LL | println!("{:?} {} {}", {}, "sss", s); + | +++++++++++++ + +error: format argument must be a string literal + --> $DIR/format-empty-block-unit-tuple-suggestion-130170.rs:7:14 + | +LL | println!({}); + | ^^ + | +help: you might be missing a string literal to format with + | +LL | println!("{:?}", {}); + | +++++++ + +error: format argument must be a string literal + --> $DIR/format-empty-block-unit-tuple-suggestion-130170.rs:9:14 + | +LL | println!(s, "sss", s, {}); + | ^ + | +help: you might be missing a string literal to format with + | +LL | println!("{} {} {} {:?}", s, "sss", s, {}); + | ++++++++++++++++ + +error: format argument must be a string literal + --> $DIR/format-empty-block-unit-tuple-suggestion-130170.rs:11:14 + | +LL | println!((), s, {}); + | ^^ + | +help: you might be missing a string literal to format with + | +LL | println!("{:?} {} {:?}", (), s, {}); + | +++++++++++++++ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/macros/issue-51848.stderr b/tests/ui/macros/issue-51848.stderr index c25bedf37b7..30b64113d73 100644 --- a/tests/ui/macros/issue-51848.stderr +++ b/tests/ui/macros/issue-51848.stderr @@ -1,8 +1,8 @@ -error: invalid format string: expected `'}'` but string was terminated +error: invalid format string: expected `}` but string was terminated --> $DIR/issue-51848.rs:6:20 | LL | println!("{"); - | -^ expected `'}'` in format string + | -^ expected `}` in format string | | | because of this opening brace ... diff --git a/tests/ui/meta/revision-bad.rs b/tests/ui/meta/revision-bad.rs index c5193b19d9e..0af5624ff9c 100644 --- a/tests/ui/meta/revision-bad.rs +++ b/tests/ui/meta/revision-bad.rs @@ -5,6 +5,7 @@ //@ revisions: foo bar //@ should-fail //@ needs-run-enabled +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped //@[foo] error-pattern:bar //@[bar] error-pattern:foo diff --git a/tests/ui/parser/fn-header-semantic-fail.rs b/tests/ui/parser/fn-header-semantic-fail.rs index 972e52d75da..202f362c81c 100644 --- a/tests/ui/parser/fn-header-semantic-fail.rs +++ b/tests/ui/parser/fn-header-semantic-fail.rs @@ -41,15 +41,15 @@ fn main() { } extern "C" { - async fn fe1(); //~ ERROR functions in `extern` blocks cannot have qualifiers - unsafe fn fe2(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers - const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers - extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers + async fn fe1(); //~ ERROR functions in `extern` blocks cannot + unsafe fn fe2(); //~ ERROR items in `extern` blocks without an `unsafe` qualifier cannot + const fn fe3(); //~ ERROR functions in `extern` blocks cannot + extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot const async unsafe extern "C" fn fe5(); //~^ ERROR functions in `extern` blocks //~| ERROR functions in `extern` blocks //~| ERROR functions in `extern` blocks //~| ERROR functions cannot be both `const` and `async` - //~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers + //~| ERROR items in `extern` blocks without an `unsafe` qualifier cannot have } } diff --git a/tests/ui/parser/fn-header-semantic-fail.stderr b/tests/ui/parser/fn-header-semantic-fail.stderr index dda42f24b32..17e880c3a79 100644 --- a/tests/ui/parser/fn-header-semantic-fail.stderr +++ b/tests/ui/parser/fn-header-semantic-fail.stderr @@ -70,77 +70,77 @@ LL | const async unsafe extern "C" fn fi5() {} | | `async` because of this | `const` because of this -error: functions in `extern` blocks cannot have qualifiers +error: functions in `extern` blocks cannot have `async` qualifier --> $DIR/fn-header-semantic-fail.rs:44:9 | LL | extern "C" { | ---------- in this `extern` block LL | async fn fe1(); - | ^^^^^ help: remove this qualifier + | ^^^^^ help: remove the `async` qualifier -error: items in unadorned `extern` blocks cannot have safety qualifiers +error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers --> $DIR/fn-header-semantic-fail.rs:45:9 | LL | unsafe fn fe2(); | ^^^^^^^^^^^^^^^^ | -help: add unsafe to this `extern` block +help: add `unsafe` to this `extern` block | LL | unsafe extern "C" { | ++++++ -error: functions in `extern` blocks cannot have qualifiers +error: functions in `extern` blocks cannot have `const` qualifier --> $DIR/fn-header-semantic-fail.rs:46:9 | LL | extern "C" { | ---------- in this `extern` block ... LL | const fn fe3(); - | ^^^^^ help: remove this qualifier + | ^^^^^ help: remove the `const` qualifier -error: functions in `extern` blocks cannot have qualifiers +error: functions in `extern` blocks cannot have `extern` qualifier --> $DIR/fn-header-semantic-fail.rs:47:9 | LL | extern "C" { | ---------- in this `extern` block ... LL | extern "C" fn fe4(); - | ^^^^^^^^^^ help: remove this qualifier + | ^^^^^^^^^^ help: remove the `extern` qualifier -error: functions in `extern` blocks cannot have qualifiers +error: functions in `extern` blocks cannot have `async` qualifier --> $DIR/fn-header-semantic-fail.rs:48:15 | LL | extern "C" { | ---------- in this `extern` block ... LL | const async unsafe extern "C" fn fe5(); - | ^^^^^ help: remove this qualifier + | ^^^^^ help: remove the `async` qualifier -error: functions in `extern` blocks cannot have qualifiers +error: functions in `extern` blocks cannot have `const` qualifier --> $DIR/fn-header-semantic-fail.rs:48:9 | LL | extern "C" { | ---------- in this `extern` block ... LL | const async unsafe extern "C" fn fe5(); - | ^^^^^ help: remove this qualifier + | ^^^^^ help: remove the `const` qualifier -error: functions in `extern` blocks cannot have qualifiers +error: functions in `extern` blocks cannot have `extern` qualifier --> $DIR/fn-header-semantic-fail.rs:48:28 | LL | extern "C" { | ---------- in this `extern` block ... LL | const async unsafe extern "C" fn fe5(); - | ^^^^^^^^^^ help: remove this qualifier + | ^^^^^^^^^^ help: remove the `extern` qualifier -error: items in unadorned `extern` blocks cannot have safety qualifiers +error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers --> $DIR/fn-header-semantic-fail.rs:48:9 | LL | const async unsafe extern "C" fn fe5(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: add unsafe to this `extern` block +help: add `unsafe` to this `extern` block | LL | unsafe extern "C" { | ++++++ diff --git a/tests/ui/parser/issues/invalid-path-sep-in-fn-definition-issue-130791.fixed b/tests/ui/parser/issues/invalid-path-sep-in-fn-definition-issue-130791.fixed new file mode 100644 index 00000000000..2c6fddccb73 --- /dev/null +++ b/tests/ui/parser/issues/invalid-path-sep-in-fn-definition-issue-130791.fixed @@ -0,0 +1,7 @@ +//@ run-rustfix + +#[allow(dead_code)] +fn invalid_path_separator<T>() {} +//~^ ERROR invalid path separator in function definition + +fn main() {} diff --git a/tests/ui/parser/issues/invalid-path-sep-in-fn-definition-issue-130791.rs b/tests/ui/parser/issues/invalid-path-sep-in-fn-definition-issue-130791.rs new file mode 100644 index 00000000000..5f690615043 --- /dev/null +++ b/tests/ui/parser/issues/invalid-path-sep-in-fn-definition-issue-130791.rs @@ -0,0 +1,7 @@ +//@ run-rustfix + +#[allow(dead_code)] +fn invalid_path_separator::<T>() {} +//~^ ERROR invalid path separator in function definition + +fn main() {} diff --git a/tests/ui/parser/issues/invalid-path-sep-in-fn-definition-issue-130791.stderr b/tests/ui/parser/issues/invalid-path-sep-in-fn-definition-issue-130791.stderr new file mode 100644 index 00000000000..3ad05050da0 --- /dev/null +++ b/tests/ui/parser/issues/invalid-path-sep-in-fn-definition-issue-130791.stderr @@ -0,0 +1,14 @@ +error: invalid path separator in function definition + --> $DIR/invalid-path-sep-in-fn-definition-issue-130791.rs:4:26 + | +LL | fn invalid_path_separator::<T>() {} + | ^^ + | +help: remove invalid path separator + | +LL - fn invalid_path_separator::<T>() {} +LL + fn invalid_path_separator<T>() {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/no-const-fn-in-extern-block.rs b/tests/ui/parser/no-const-fn-in-extern-block.rs index 3ad9ba006d3..0f1e44ced10 100644 --- a/tests/ui/parser/no-const-fn-in-extern-block.rs +++ b/tests/ui/parser/no-const-fn-in-extern-block.rs @@ -1,9 +1,9 @@ extern "C" { const fn foo(); - //~^ ERROR functions in `extern` blocks cannot have qualifiers + //~^ ERROR functions in `extern` blocks cannot const unsafe fn bar(); - //~^ ERROR functions in `extern` blocks cannot have qualifiers - //~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers + //~^ ERROR functions in `extern` blocks cannot + //~| ERROR items in `extern` blocks without an `unsafe` qualifier cannot } fn main() {} diff --git a/tests/ui/parser/no-const-fn-in-extern-block.stderr b/tests/ui/parser/no-const-fn-in-extern-block.stderr index 8c23824a708..46f845e85c2 100644 --- a/tests/ui/parser/no-const-fn-in-extern-block.stderr +++ b/tests/ui/parser/no-const-fn-in-extern-block.stderr @@ -1,27 +1,27 @@ -error: functions in `extern` blocks cannot have qualifiers +error: functions in `extern` blocks cannot have `const` qualifier --> $DIR/no-const-fn-in-extern-block.rs:2:5 | LL | extern "C" { | ---------- in this `extern` block LL | const fn foo(); - | ^^^^^ help: remove this qualifier + | ^^^^^ help: remove the `const` qualifier -error: functions in `extern` blocks cannot have qualifiers +error: functions in `extern` blocks cannot have `const` qualifier --> $DIR/no-const-fn-in-extern-block.rs:4:5 | LL | extern "C" { | ---------- in this `extern` block ... LL | const unsafe fn bar(); - | ^^^^^ help: remove this qualifier + | ^^^^^ help: remove the `const` qualifier -error: items in unadorned `extern` blocks cannot have safety qualifiers +error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers --> $DIR/no-const-fn-in-extern-block.rs:4:5 | LL | const unsafe fn bar(); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: add unsafe to this `extern` block +help: add `unsafe` to this `extern` block | LL | unsafe extern "C" { | ++++++ diff --git a/tests/ui/precondition-checks/alignment.rs b/tests/ui/precondition-checks/alignment.rs new file mode 100644 index 00000000000..92400528fa0 --- /dev/null +++ b/tests/ui/precondition-checks/alignment.rs @@ -0,0 +1,11 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: Alignment::new_unchecked requires + +#![feature(ptr_alignment_type)] + +fn main() { + unsafe { + std::ptr::Alignment::new_unchecked(0); + } +} diff --git a/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs b/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs new file mode 100644 index 00000000000..30c6f79fb08 --- /dev/null +++ b/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs @@ -0,0 +1,11 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: `ascii::Char::digit_unchecked` input cannot exceed 9 + +#![feature(ascii_char)] + +fn main() { + unsafe { + std::ascii::Char::digit_unchecked(b'a'); + } +} diff --git a/tests/ui/precondition-checks/assert_unchecked.rs b/tests/ui/precondition-checks/assert_unchecked.rs new file mode 100644 index 00000000000..22b2b414550 --- /dev/null +++ b/tests/ui/precondition-checks/assert_unchecked.rs @@ -0,0 +1,9 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: hint::assert_unchecked must never be called when the condition is false + +fn main() { + unsafe { + std::hint::assert_unchecked(false); + } +} diff --git a/tests/ui/precondition-checks/char-from_u32_unchecked.rs b/tests/ui/precondition-checks/char-from_u32_unchecked.rs new file mode 100644 index 00000000000..d950f20c772 --- /dev/null +++ b/tests/ui/precondition-checks/char-from_u32_unchecked.rs @@ -0,0 +1,9 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: invalid value for `char` + +fn main() { + unsafe { + char::from_u32_unchecked(0xD801); + } +} diff --git a/tests/ui/precondition-checks/copy-nonoverlapping.rs b/tests/ui/precondition-checks/copy-nonoverlapping.rs new file mode 100644 index 00000000000..81018e4bff3 --- /dev/null +++ b/tests/ui/precondition-checks/copy-nonoverlapping.rs @@ -0,0 +1,25 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: ptr::copy_nonoverlapping requires +//@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping + +use std::ptr; + +fn main() { + let src = [0u16; 3]; + let mut dst = [0u16; 3]; + let src = src.as_ptr(); + let dst = dst.as_mut_ptr(); + unsafe { + #[cfg(null_src)] + ptr::copy_nonoverlapping(ptr::null(), dst, 1); + #[cfg(null_dst)] + ptr::copy_nonoverlapping(src, ptr::null_mut(), 1); + #[cfg(misaligned_src)] + ptr::copy_nonoverlapping(src.byte_add(1), dst, 1); + #[cfg(misaligned_dst)] + ptr::copy_nonoverlapping(src, dst.byte_add(1), 1); + #[cfg(overlapping)] + ptr::copy_nonoverlapping(dst, dst.add(1), 2); + } +} diff --git a/tests/ui/precondition-checks/copy.rs b/tests/ui/precondition-checks/copy.rs new file mode 100644 index 00000000000..694853f950a --- /dev/null +++ b/tests/ui/precondition-checks/copy.rs @@ -0,0 +1,23 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: ptr::copy requires +//@ revisions: null_src null_dst misaligned_src misaligned_dst + +use std::ptr; + +fn main() { + let src = [0u16; 3]; + let mut dst = [0u16; 3]; + let src = src.as_ptr(); + let dst = dst.as_mut_ptr(); + unsafe { + #[cfg(null_src)] + ptr::copy(ptr::null(), dst, 1); + #[cfg(null_dst)] + ptr::copy(src, ptr::null_mut(), 1); + #[cfg(misaligned_src)] + ptr::copy(src.byte_add(1), dst, 1); + #[cfg(misaligned_dst)] + ptr::copy(src, dst.byte_add(1), 1); + } +} diff --git a/tests/ui/precondition-checks/layout.rs b/tests/ui/precondition-checks/layout.rs new file mode 100644 index 00000000000..4fd1bbc4a99 --- /dev/null +++ b/tests/ui/precondition-checks/layout.rs @@ -0,0 +1,15 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: Layout::from_size_align_unchecked requires +//@ revisions: toolarge badalign +//@[toolarge] compile-flags: --cfg toolarge +//@[badalign] compile-flags: --cfg badalign + +fn main() { + unsafe { + #[cfg(toolarge)] + std::alloc::Layout::from_size_align_unchecked(isize::MAX as usize, 2); + #[cfg(badalign)] + std::alloc::Layout::from_size_align_unchecked(1, 3); + } +} diff --git a/tests/ui/precondition-checks/misaligned-slice.rs b/tests/ui/precondition-checks/misaligned-slice.rs deleted file mode 100644 index 2963a0b5e63..00000000000 --- a/tests/ui/precondition-checks/misaligned-slice.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-fail -//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts -//@ ignore-debug - -fn main() { - unsafe { - let _s: &[u64] = std::slice::from_raw_parts(1usize as *const u64, 0); - } -} diff --git a/tests/ui/precondition-checks/nonnull.rs b/tests/ui/precondition-checks/nonnull.rs new file mode 100644 index 00000000000..6b8edd4e582 --- /dev/null +++ b/tests/ui/precondition-checks/nonnull.rs @@ -0,0 +1,9 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: NonNull::new_unchecked requires + +fn main() { + unsafe { + std::ptr::NonNull::new_unchecked(std::ptr::null_mut::<u8>()); + } +} diff --git a/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs b/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs new file mode 100644 index 00000000000..46ce7dc356f --- /dev/null +++ b/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs @@ -0,0 +1,12 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: NonZero::from_mut_unchecked requires + +#![feature(nonzero_from_mut)] + +fn main() { + unsafe { + let mut num = 0u8; + std::num::NonZeroU8::from_mut_unchecked(&mut num); + } +} diff --git a/tests/ui/precondition-checks/nonzero-new_unchecked.rs b/tests/ui/precondition-checks/nonzero-new_unchecked.rs new file mode 100644 index 00000000000..7827a42844f --- /dev/null +++ b/tests/ui/precondition-checks/nonzero-new_unchecked.rs @@ -0,0 +1,9 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: NonZero::new_unchecked requires + +fn main() { + unsafe { + std::num::NonZeroU8::new_unchecked(0); + } +} diff --git a/tests/ui/precondition-checks/null-slice.rs b/tests/ui/precondition-checks/null-slice.rs deleted file mode 100644 index 280960358b7..00000000000 --- a/tests/ui/precondition-checks/null-slice.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-fail -//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts -//@ ignore-debug - -fn main() { - unsafe { - let _s: &[u8] = std::slice::from_raw_parts(std::ptr::null(), 0); - } -} diff --git a/tests/ui/precondition-checks/out-of-bounds-get-unchecked.rs b/tests/ui/precondition-checks/out-of-bounds-get-unchecked.rs deleted file mode 100644 index 011e92183fa..00000000000 --- a/tests/ui/precondition-checks/out-of-bounds-get-unchecked.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ run-fail -//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: slice::get_unchecked requires -//@ ignore-debug - -fn main() { - unsafe { - let sli: &[u8] = &[0]; - sli.get_unchecked(1); - } -} diff --git a/tests/ui/precondition-checks/read.rs b/tests/ui/precondition-checks/read.rs new file mode 100644 index 00000000000..ab9921a0cee --- /dev/null +++ b/tests/ui/precondition-checks/read.rs @@ -0,0 +1,18 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: ptr::read requires +//@ revisions: null misaligned +//@ ignore-test + +use std::ptr; + +fn main() { + let src = [0u16; 2]; + let src = src.as_ptr(); + unsafe { + #[cfg(null)] + ptr::read(ptr::null::<u8>()); + #[cfg(misaligned)] + ptr::read(src.byte_add(1)); + } +} diff --git a/tests/ui/precondition-checks/read_volatile.rs b/tests/ui/precondition-checks/read_volatile.rs new file mode 100644 index 00000000000..e14881d0290 --- /dev/null +++ b/tests/ui/precondition-checks/read_volatile.rs @@ -0,0 +1,17 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: ptr::read_volatile requires +//@ revisions: null misaligned + +use std::ptr; + +fn main() { + let src = [0u16; 2]; + let src = src.as_ptr(); + unsafe { + #[cfg(null)] + ptr::read_volatile(ptr::null::<u8>()); + #[cfg(misaligned)] + ptr::read_volatile(src.byte_add(1)); + } +} diff --git a/tests/ui/precondition-checks/replace.rs b/tests/ui/precondition-checks/replace.rs new file mode 100644 index 00000000000..2808cee7b64 --- /dev/null +++ b/tests/ui/precondition-checks/replace.rs @@ -0,0 +1,17 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: ptr::replace requires +//@ revisions: null misaligned + +use std::ptr; + +fn main() { + let mut dst = [0u16; 2]; + let dst = dst.as_mut_ptr(); + unsafe { + #[cfg(null)] + ptr::replace(ptr::null_mut::<u8>(), 1); + #[cfg(misaligned)] + ptr::replace(dst.byte_add(1), 1u16); + } +} diff --git a/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs b/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs new file mode 100644 index 00000000000..3801639e255 --- /dev/null +++ b/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs @@ -0,0 +1,16 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts_mut requires +//@ revisions: null misaligned toolarge + +fn main() { + unsafe { + #[cfg(null)] + let _s: &mut [u8] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); + #[cfg(misaligned)] + let _s: &mut [u16] = std::slice::from_raw_parts_mut(1usize as *mut u16, 0); + #[cfg(toolarge)] + let _s: &mut [u16] = + std::slice::from_raw_parts_mut(2usize as *mut u16, isize::MAX as usize); + } +} diff --git a/tests/ui/precondition-checks/slice-from-raw-parts.rs b/tests/ui/precondition-checks/slice-from-raw-parts.rs new file mode 100644 index 00000000000..a3690fa045e --- /dev/null +++ b/tests/ui/precondition-checks/slice-from-raw-parts.rs @@ -0,0 +1,15 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts requires +//@ revisions: null misaligned toolarge + +fn main() { + unsafe { + #[cfg(null)] + let _s: &[u8] = std::slice::from_raw_parts(std::ptr::null(), 0); + #[cfg(misaligned)] + let _s: &[u16] = std::slice::from_raw_parts(1usize as *const u16, 0); + #[cfg(toolarge)] + let _s: &[u16] = std::slice::from_raw_parts(2usize as *const u16, isize::MAX as usize); + } +} diff --git a/tests/ui/precondition-checks/slice-get_unchecked.rs b/tests/ui/precondition-checks/slice-get_unchecked.rs new file mode 100644 index 00000000000..1d8188fb953 --- /dev/null +++ b/tests/ui/precondition-checks/slice-get_unchecked.rs @@ -0,0 +1,20 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: slice::get_unchecked requires +//@ revisions: usize range range_to range_from backwards_range + +fn main() { + unsafe { + let s = &[0]; + #[cfg(usize)] + s.get_unchecked(1); + #[cfg(range)] + s.get_unchecked(1..2); + #[cfg(range_to)] + s.get_unchecked(..2); + #[cfg(range_from)] + s.get_unchecked(2..); + #[cfg(backwards_range)] + s.get_unchecked(1..0); + } +} diff --git a/tests/ui/precondition-checks/slice-get_unchecked_mut.rs b/tests/ui/precondition-checks/slice-get_unchecked_mut.rs new file mode 100644 index 00000000000..34c1454af43 --- /dev/null +++ b/tests/ui/precondition-checks/slice-get_unchecked_mut.rs @@ -0,0 +1,20 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: slice::get_unchecked_mut requires +//@ revisions: usize range range_to range_from backwards_range + +fn main() { + unsafe { + let mut s = &mut [0]; + #[cfg(usize)] + s.get_unchecked_mut(1); + #[cfg(range)] + s.get_unchecked_mut(1..2); + #[cfg(range_to)] + s.get_unchecked_mut(..2); + #[cfg(range_from)] + s.get_unchecked_mut(2..); + #[cfg(backwards_range)] + s.get_unchecked_mut(1..0); + } +} diff --git a/tests/ui/precondition-checks/slice-swap_unchecked.rs b/tests/ui/precondition-checks/slice-swap_unchecked.rs new file mode 100644 index 00000000000..227a49091ec --- /dev/null +++ b/tests/ui/precondition-checks/slice-swap_unchecked.rs @@ -0,0 +1,14 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: index out of bounds: the len is 2 but the index is 2 +//@ revisions: oob_a oob_b + +fn main() { + let mut pair = [0u8; 2]; + unsafe { + #[cfg(oob_a)] + pair.swap(0, 2); + #[cfg(oob_b)] + pair.swap(2, 0); + } +} diff --git a/tests/ui/precondition-checks/str-get_unchecked.rs b/tests/ui/precondition-checks/str-get_unchecked.rs new file mode 100644 index 00000000000..14d17f997ec --- /dev/null +++ b/tests/ui/precondition-checks/str-get_unchecked.rs @@ -0,0 +1,18 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: str::get_unchecked requires +//@ revisions: range range_to range_from backwards_range + +fn main() { + unsafe { + let s = "💅"; + #[cfg(range)] + s.get_unchecked(4..5); + #[cfg(range_to)] + s.get_unchecked(..5); + #[cfg(range_from)] + s.get_unchecked(5..); + #[cfg(backwards_range)] + s.get_unchecked(1..0); + } +} diff --git a/tests/ui/precondition-checks/str-get_unchecked_mut.rs b/tests/ui/precondition-checks/str-get_unchecked_mut.rs new file mode 100644 index 00000000000..ca1b1690055 --- /dev/null +++ b/tests/ui/precondition-checks/str-get_unchecked_mut.rs @@ -0,0 +1,19 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: str::get_unchecked_mut requires +//@ revisions: range range_to range_from backwards_range + +fn main() { + unsafe { + let mut s: String = "💅".chars().collect(); + let mut s: &mut str = &mut s; + #[cfg(range)] + s.get_unchecked_mut(4..5); + #[cfg(range_to)] + s.get_unchecked_mut(..5); + #[cfg(range_from)] + s.get_unchecked_mut(5..); + #[cfg(backwards_range)] + s.get_unchecked_mut(1..0); + } +} diff --git a/tests/ui/precondition-checks/swap-nonoverlapping.rs b/tests/ui/precondition-checks/swap-nonoverlapping.rs new file mode 100644 index 00000000000..52e4a3c870b --- /dev/null +++ b/tests/ui/precondition-checks/swap-nonoverlapping.rs @@ -0,0 +1,25 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: ptr::swap_nonoverlapping requires +//@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping + +use std::ptr; + +fn main() { + let mut src = [0u16; 3]; + let mut dst = [0u16; 3]; + let src = src.as_mut_ptr(); + let dst = dst.as_mut_ptr(); + unsafe { + #[cfg(null_src)] + ptr::swap_nonoverlapping(ptr::null_mut(), dst, 1); + #[cfg(null_dst)] + ptr::swap_nonoverlapping(src, ptr::null_mut(), 1); + #[cfg(misaligned_src)] + ptr::swap_nonoverlapping(src.byte_add(1), dst, 1); + #[cfg(misaligned_dst)] + ptr::swap_nonoverlapping(src, dst.byte_add(1), 1); + #[cfg(overlapping)] + ptr::swap_nonoverlapping(dst, dst.add(1), 2); + } +} diff --git a/tests/ui/precondition-checks/unchecked_add.rs b/tests/ui/precondition-checks/unchecked_add.rs new file mode 100644 index 00000000000..f44a6ea32ad --- /dev/null +++ b/tests/ui/precondition-checks/unchecked_add.rs @@ -0,0 +1,9 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_add cannot overflow + +fn main() { + unsafe { + 1u8.unchecked_add(u8::MAX); + } +} diff --git a/tests/ui/precondition-checks/unchecked_mul.rs b/tests/ui/precondition-checks/unchecked_mul.rs new file mode 100644 index 00000000000..66655dda136 --- /dev/null +++ b/tests/ui/precondition-checks/unchecked_mul.rs @@ -0,0 +1,9 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_add cannot overflow + +fn main() { + unsafe { + 2u8.unchecked_add(u8::MAX); + } +} diff --git a/tests/ui/precondition-checks/unchecked_shl.rs b/tests/ui/precondition-checks/unchecked_shl.rs new file mode 100644 index 00000000000..1c96db0b1ec --- /dev/null +++ b/tests/ui/precondition-checks/unchecked_shl.rs @@ -0,0 +1,11 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_shl cannot overflow + +#![feature(unchecked_shifts)] + +fn main() { + unsafe { + 0u8.unchecked_shl(u8::BITS); + } +} diff --git a/tests/ui/precondition-checks/unchecked_shr.rs b/tests/ui/precondition-checks/unchecked_shr.rs new file mode 100644 index 00000000000..4a6d9ffb1d3 --- /dev/null +++ b/tests/ui/precondition-checks/unchecked_shr.rs @@ -0,0 +1,11 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_shr cannot overflow + +#![feature(unchecked_shifts)] + +fn main() { + unsafe { + 0u8.unchecked_shr(u8::BITS); + } +} diff --git a/tests/ui/precondition-checks/unchecked_sub.rs b/tests/ui/precondition-checks/unchecked_sub.rs new file mode 100644 index 00000000000..545dde0e278 --- /dev/null +++ b/tests/ui/precondition-checks/unchecked_sub.rs @@ -0,0 +1,9 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_sub cannot overflow + +fn main() { + unsafe { + 0u8.unchecked_sub(1u8); + } +} diff --git a/tests/ui/precondition-checks/unreachable_unchecked.rs b/tests/ui/precondition-checks/unreachable_unchecked.rs new file mode 100644 index 00000000000..2435450c4b5 --- /dev/null +++ b/tests/ui/precondition-checks/unreachable_unchecked.rs @@ -0,0 +1,9 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached + +fn main() { + unsafe { + std::hint::unreachable_unchecked(); + } +} diff --git a/tests/ui/precondition-checks/write.rs b/tests/ui/precondition-checks/write.rs new file mode 100644 index 00000000000..f76e776fcf3 --- /dev/null +++ b/tests/ui/precondition-checks/write.rs @@ -0,0 +1,18 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: ptr::write requires +//@ revisions: null misaligned +//@ ignore-test + +use std::ptr; + +fn main() { + let mut dst = [0u16; 2]; + let mut dst = dst.as_mut_ptr(); + unsafe { + #[cfg(null)] + ptr::write(ptr::null_mut::<u8>(), 1u8); + #[cfg(misaligned)] + ptr::write(dst.byte_add(1), 1u16); + } +} diff --git a/tests/ui/precondition-checks/write_bytes.rs b/tests/ui/precondition-checks/write_bytes.rs new file mode 100644 index 00000000000..3f64be9d1ee --- /dev/null +++ b/tests/ui/precondition-checks/write_bytes.rs @@ -0,0 +1,18 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: ptr::write requires +//@ revisions: null misaligned +//@ ignore-test + +use std::ptr; + +fn main() { + let mut dst = [0u16; 2]; + let mut dst = dst.as_mut_ptr(); + unsafe { + #[cfg(null)] + ptr::write_bytes(ptr::null_mut::<u8>(), 1u8, 2); + #[cfg(misaligned)] + ptr::write_bytes(dst.byte_add(1), 1u8, 2); + } +} diff --git a/tests/ui/precondition-checks/write_volatile.rs b/tests/ui/precondition-checks/write_volatile.rs new file mode 100644 index 00000000000..ac0b89b5ecf --- /dev/null +++ b/tests/ui/precondition-checks/write_volatile.rs @@ -0,0 +1,17 @@ +//@ run-fail +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: unsafe precondition(s) violated: ptr::write_volatile requires +//@ revisions: null misaligned + +use std::ptr; + +fn main() { + let mut dst = [0u16; 2]; + let mut dst = dst.as_mut_ptr(); + unsafe { + #[cfg(null)] + ptr::write_volatile(ptr::null_mut::<u8>(), 1u8); + #[cfg(misaligned)] + ptr::write_volatile(dst.byte_add(1), 1u16); + } +} diff --git a/tests/ui/precondition-checks/zero-size-null.rs b/tests/ui/precondition-checks/zero-size-null.rs new file mode 100644 index 00000000000..43a81175f94 --- /dev/null +++ b/tests/ui/precondition-checks/zero-size-null.rs @@ -0,0 +1,21 @@ +// Test that none of the precondition checks panic on zero-sized reads or writes through null. + +//@ run-pass +//@ compile-flags: -Zmir-opt-level=0 -Copt-level=0 -Cdebug-assertions=yes + +use std::ptr; + +fn main() { + unsafe { + ptr::copy_nonoverlapping::<u8>(ptr::null(), ptr::null_mut(), 0); + ptr::copy_nonoverlapping::<()>(ptr::null(), ptr::null_mut(), 123); + ptr::copy::<u8>(ptr::null(), ptr::null_mut(), 0); + ptr::copy::<()>(ptr::null(), ptr::null_mut(), 123); + ptr::swap::<()>(ptr::null_mut(), ptr::null_mut()); + ptr::replace::<()>(ptr::null_mut(), ()); + ptr::read::<()>(ptr::null()); + ptr::write::<()>(ptr::null_mut(), ()); + ptr::read_volatile::<()>(ptr::null()); + ptr::write_volatile::<()>(ptr::null_mut(), ()); + } +} diff --git a/tests/ui/resolve/issue-111312.rs b/tests/ui/resolve/issue-111312.rs index 79c6f67dadd..574e9870cd2 100644 --- a/tests/ui/resolve/issue-111312.rs +++ b/tests/ui/resolve/issue-111312.rs @@ -8,6 +8,5 @@ trait HasNot {} fn main() { HasNot::has(); - //~^ ERROR trait objects must include the `dyn` keyword - //~| ERROR no function or associated item named `has` found for trait `HasNot` + //~^ ERROR expected a type, found a trait } diff --git a/tests/ui/resolve/issue-111312.stderr b/tests/ui/resolve/issue-111312.stderr index 431802ead30..a1f8d6bc46c 100644 --- a/tests/ui/resolve/issue-111312.stderr +++ b/tests/ui/resolve/issue-111312.stderr @@ -1,27 +1,14 @@ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/issue-111312.rs:10:5 | LL | HasNot::has(); | ^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | <dyn HasNot>::has(); | ++++ + -error[E0599]: no function or associated item named `has` found for trait `HasNot` - --> $DIR/issue-111312.rs:10:13 - | -LL | HasNot::has(); - | ^^^ function or associated item not found in `HasNot` - | -note: `Has` defines an item `has` - --> $DIR/issue-111312.rs:3:1 - | -LL | trait Has { - | ^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0599, E0782. -For more information about an error, try `rustc --explain E0599`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/resolve/issue-111727.rs b/tests/ui/resolve/issue-111727.rs index fcab924b809..a5c2c0ca8c6 100644 --- a/tests/ui/resolve/issue-111727.rs +++ b/tests/ui/resolve/issue-111727.rs @@ -2,6 +2,5 @@ fn main() { std::any::Any::create(); - //~^ ERROR trait objects must include the `dyn` keyword - //~| ERROR no function or associated item named `create` found for trait `Any` + //~^ ERROR expected a type, found a trait } diff --git a/tests/ui/resolve/issue-111727.stderr b/tests/ui/resolve/issue-111727.stderr index 1ef5a1a1d5e..71ea989ac34 100644 --- a/tests/ui/resolve/issue-111727.stderr +++ b/tests/ui/resolve/issue-111727.stderr @@ -1,21 +1,14 @@ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/issue-111727.rs:4:5 | LL | std::any::Any::create(); | ^^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | <dyn std::any::Any>::create(); | ++++ + -error[E0599]: no function or associated item named `create` found for trait `Any` - --> $DIR/issue-111727.rs:4:20 - | -LL | std::any::Any::create(); - | ^^^^^^ function or associated item not found in `Any` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0599, E0782. -For more information about an error, try `rustc --explain E0599`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/runtime/stdout-before-main.rs b/tests/ui/runtime/stdout-before-main.rs new file mode 100644 index 00000000000..26c734d3e9e --- /dev/null +++ b/tests/ui/runtime/stdout-before-main.rs @@ -0,0 +1,24 @@ +//@ run-pass +//@ check-run-results +//@ only-gnu +//@ only-linux +// +// Regression test for #130210. +// .init_array doesn't work everywhere, so we limit the test to just GNU/Linux. + +use std::ffi::c_int; +use std::thread; + +#[used] +#[link_section = ".init_array"] +static INIT: extern "C" fn(c_int, *const *const u8, *const *const u8) = { + extern "C" fn init(_argc: c_int, _argv: *const *const u8, _envp: *const *const u8) { + print!("Hello from before "); + } + + init +}; + +fn main() { + println!("{}!", thread::current().name().unwrap()); +} diff --git a/tests/ui/runtime/stdout-before-main.run.stdout b/tests/ui/runtime/stdout-before-main.run.stdout new file mode 100644 index 00000000000..d7a3a4389ec --- /dev/null +++ b/tests/ui/runtime/stdout-before-main.run.stdout @@ -0,0 +1 @@ +Hello from before main! diff --git a/tests/ui/rust-2021/ice-return-unsized-can-impl-2.rs b/tests/ui/rust-2021/ice-return-unsized-can-impl-2.rs new file mode 100644 index 00000000000..e57e9ce4844 --- /dev/null +++ b/tests/ui/rust-2021/ice-return-unsized-can-impl-2.rs @@ -0,0 +1,15 @@ +// Doesn't trigger ICE when returning unsized trait that can be impl +// issue https://github.com/rust-lang/rust/issues/125512 +//@ edition:2021 +#![feature(dyn_compatible_for_dispatch)] +trait B { + fn f(a: A) -> A; + //~^ ERROR: expected a type, found a trait + //~| ERROR: expected a type, found a trait +} +trait A { + fn concrete(b: B) -> B; + //~^ ERROR: expected a type, found a trait + //~| ERROR: expected a type, found a trait +} +fn main() {} diff --git a/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr b/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr new file mode 100644 index 00000000000..b8a9a5c8129 --- /dev/null +++ b/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr @@ -0,0 +1,57 @@ +error[E0782]: expected a type, found a trait + --> $DIR/ice-return-unsized-can-impl-2.rs:11:20 + | +LL | fn concrete(b: B) -> B; + | ^ + | + = note: `B` it is dyn-incompatible, so it can't be `dyn` +help: use a new generic type parameter, constrained by `B` + | +LL | fn concrete<T: B>(b: T) -> B; + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn concrete(b: impl B) -> B; + | ++++ + +error[E0782]: expected a type, found a trait + --> $DIR/ice-return-unsized-can-impl-2.rs:11:26 + | +LL | fn concrete(b: B) -> B; + | ^ + | +help: `B` is dyn-incompatible, use `impl B` to return an opaque type, as long as you return a single underlying type + | +LL | fn concrete(b: B) -> impl B; + | ++++ + +error[E0782]: expected a type, found a trait + --> $DIR/ice-return-unsized-can-impl-2.rs:6:13 + | +LL | fn f(a: A) -> A; + | ^ + | + = note: `A` it is dyn-incompatible, so it can't be `dyn` +help: use a new generic type parameter, constrained by `A` + | +LL | fn f<T: A>(a: T) -> A; + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn f(a: impl A) -> A; + | ++++ + +error[E0782]: expected a type, found a trait + --> $DIR/ice-return-unsized-can-impl-2.rs:6:19 + | +LL | fn f(a: A) -> A; + | ^ + | +help: `A` is dyn-incompatible, use `impl A` to return an opaque type, as long as you return a single underlying type + | +LL | fn f(a: A) -> impl A; + | ++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/rust-2021/ice-return-unsized-can-impl.rs b/tests/ui/rust-2021/ice-return-unsized-can-impl.rs new file mode 100644 index 00000000000..055b11b4424 --- /dev/null +++ b/tests/ui/rust-2021/ice-return-unsized-can-impl.rs @@ -0,0 +1,16 @@ +// Doesn't trigger ICE when returning unsized trait that can be impl +// issue https://github.com/rust-lang/rust/issues/120482 +//@ edition:2021 +#![feature(dyn_compatible_for_dispatch)] + +trait B { + fn bar(&self, x: &Self); +} + +trait A { + fn g(new: B) -> B; + //~^ ERROR: expected a type, found a trait + //~| ERROR: expected a type, found a trait +} + +fn main() {} diff --git a/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr b/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr new file mode 100644 index 00000000000..c0969570e92 --- /dev/null +++ b/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr @@ -0,0 +1,30 @@ +error[E0782]: expected a type, found a trait + --> $DIR/ice-return-unsized-can-impl.rs:11:15 + | +LL | fn g(new: B) -> B; + | ^ + | + = note: `B` it is dyn-incompatible, so it can't be `dyn` +help: use a new generic type parameter, constrained by `B` + | +LL | fn g<T: B>(new: T) -> B; + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn g(new: impl B) -> B; + | ++++ + +error[E0782]: expected a type, found a trait + --> $DIR/ice-return-unsized-can-impl.rs:11:21 + | +LL | fn g(new: B) -> B; + | ^ + | +help: `B` is dyn-incompatible, use `impl B` to return an opaque type, as long as you return a single underlying type + | +LL | fn g(new: B) -> impl B; + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/rust-2021/ice-unsized-fn-params-2.rs b/tests/ui/rust-2021/ice-unsized-fn-params-2.rs new file mode 100644 index 00000000000..2b4f7bd088f --- /dev/null +++ b/tests/ui/rust-2021/ice-unsized-fn-params-2.rs @@ -0,0 +1,12 @@ +//@ edition:2021 +// Test that it doesn't trigger an ICE when using an unsized fn params. +// https://github.com/rust-lang/rust/issues/120241 +#![feature(dyn_compatible_for_dispatch)] +#![feature(unsized_fn_params)] + +fn guard(_s: Copy) -> bool { + //~^ ERROR: expected a type, found a trait + panic!() +} + +fn main() {} diff --git a/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr b/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr new file mode 100644 index 00000000000..d35c8ab3e42 --- /dev/null +++ b/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr @@ -0,0 +1,19 @@ +error[E0782]: expected a type, found a trait + --> $DIR/ice-unsized-fn-params-2.rs:7:14 + | +LL | fn guard(_s: Copy) -> bool { + | ^^^^ + | + = note: `Copy` it is dyn-incompatible, so it can't be `dyn` +help: use a new generic type parameter, constrained by `Copy` + | +LL | fn guard<T: Copy>(_s: T) -> bool { + | +++++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn guard(_s: impl Copy) -> bool { + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/rust-2021/ice-unsized-fn-params.rs b/tests/ui/rust-2021/ice-unsized-fn-params.rs new file mode 100644 index 00000000000..6d8c1c3f152 --- /dev/null +++ b/tests/ui/rust-2021/ice-unsized-fn-params.rs @@ -0,0 +1,18 @@ +//@ edition:2021 +// Test that it doesn't trigger an ICE when using an unsized fn params. +// https://github.com/rust-lang/rust/issues/120241 +#![feature(dyn_compatible_for_dispatch)] + +trait B { + fn f(a: A) -> A; + //~^ ERROR: expected a type, found a trait + //~| ERROR: expected a type, found a trait +} + +trait A { + fn g(b: B) -> B; + //~^ ERROR: expected a type, found a trait + //~| ERROR: expected a type, found a trait +} + +fn main() {} diff --git a/tests/ui/rust-2021/ice-unsized-fn-params.stderr b/tests/ui/rust-2021/ice-unsized-fn-params.stderr new file mode 100644 index 00000000000..d56e9981a28 --- /dev/null +++ b/tests/ui/rust-2021/ice-unsized-fn-params.stderr @@ -0,0 +1,57 @@ +error[E0782]: expected a type, found a trait + --> $DIR/ice-unsized-fn-params.rs:13:13 + | +LL | fn g(b: B) -> B; + | ^ + | + = note: `B` it is dyn-incompatible, so it can't be `dyn` +help: use a new generic type parameter, constrained by `B` + | +LL | fn g<T: B>(b: T) -> B; + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn g(b: impl B) -> B; + | ++++ + +error[E0782]: expected a type, found a trait + --> $DIR/ice-unsized-fn-params.rs:13:19 + | +LL | fn g(b: B) -> B; + | ^ + | +help: `B` is dyn-incompatible, use `impl B` to return an opaque type, as long as you return a single underlying type + | +LL | fn g(b: B) -> impl B; + | ++++ + +error[E0782]: expected a type, found a trait + --> $DIR/ice-unsized-fn-params.rs:7:13 + | +LL | fn f(a: A) -> A; + | ^ + | + = note: `A` it is dyn-incompatible, so it can't be `dyn` +help: use a new generic type parameter, constrained by `A` + | +LL | fn f<T: A>(a: T) -> A; + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn f(a: impl A) -> A; + | ++++ + +error[E0782]: expected a type, found a trait + --> $DIR/ice-unsized-fn-params.rs:7:19 + | +LL | fn f(a: A) -> A; + | ^ + | +help: `A` is dyn-incompatible, use `impl A` to return an opaque type, as long as you return a single underlying type + | +LL | fn f(a: A) -> impl A; + | ++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr index 93797987286..ddc5477116f 100644 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr @@ -1,21 +1,21 @@ -error: items in unadorned `extern` blocks cannot have safety qualifiers +error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:5 | LL | safe static TEST1: i32; | ^^^^^^^^^^^^^^^^^^^^^^^ | -help: add unsafe to this `extern` block +help: add `unsafe` to this `extern` block | LL | unsafe extern "C" { | ++++++ -error: items in unadorned `extern` blocks cannot have safety qualifiers +error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5 | LL | safe fn test1(i: i32); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: add unsafe to this `extern` block +help: add `unsafe` to this `extern` block | LL | unsafe extern "C" { | ++++++ diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr index e9db6006c0b..ae7b4cd47c0 100644 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr @@ -10,24 +10,24 @@ LL | | LL | | } | |_^ -error: items in unadorned `extern` blocks cannot have safety qualifiers +error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:5 | LL | safe static TEST1: i32; | ^^^^^^^^^^^^^^^^^^^^^^^ | -help: add unsafe to this `extern` block +help: add `unsafe` to this `extern` block | LL | unsafe extern "C" { | ++++++ -error: items in unadorned `extern` blocks cannot have safety qualifiers +error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5 | LL | safe fn test1(i: i32); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: add unsafe to this `extern` block +help: add `unsafe` to this `extern` block | LL | unsafe extern "C" { | ++++++ diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs index 4badb50b267..89415a69f08 100644 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs @@ -6,9 +6,9 @@ extern "C" { //[edition2024]~^ ERROR extern blocks must be unsafe safe static TEST1: i32; - //~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers + //~^ ERROR items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers safe fn test1(i: i32); - //~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers + //~^ ERROR items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers } fn test2() { diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed index 857d34eef85..79983a64c2b 100644 --- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed @@ -3,7 +3,7 @@ #![allow(dead_code)] unsafe extern "C" { - unsafe fn foo(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers + unsafe fn foo(); //~ ERROR items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers } fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs index edab9850d79..cb84d3d1411 100644 --- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs @@ -3,7 +3,7 @@ #![allow(dead_code)] extern "C" { - unsafe fn foo(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers + unsafe fn foo(); //~ ERROR items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers } fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr index 073245e650b..cf1c0012f4d 100644 --- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr @@ -1,10 +1,10 @@ -error: items in unadorned `extern` blocks cannot have safety qualifiers +error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers --> $DIR/unsafe-on-extern-block-issue-126756.rs:6:5 | LL | unsafe fn foo(); | ^^^^^^^^^^^^^^^^ | -help: add unsafe to this `extern` block +help: add `unsafe` to this `extern` block | LL | unsafe extern "C" { | ++++++ diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs index 37afab6b643..10b4781eb04 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs @@ -2,24 +2,21 @@ #![allow(bare_trait_objects)] trait A: Sized { fn f(a: A) -> A; - //~^ ERROR trait objects must include the `dyn` keyword - //~| ERROR trait objects must include the `dyn` keyword + //~^ ERROR expected a type, found a trait + //~| ERROR expected a type, found a trait //~| ERROR associated item referring to unboxed trait object for its own trait - //~| ERROR the trait `A` cannot be made into an object } trait B { fn f(b: B) -> B; - //~^ ERROR trait objects must include the `dyn` keyword - //~| ERROR trait objects must include the `dyn` keyword + //~^ ERROR expected a type, found a trait + //~| ERROR expected a type, found a trait //~| ERROR associated item referring to unboxed trait object for its own trait - //~| ERROR the trait `B` cannot be made into an object } trait C { fn f(&self, c: C) -> C; - //~^ ERROR trait objects must include the `dyn` keyword - //~| ERROR trait objects must include the `dyn` keyword + //~^ ERROR expected a type, found a trait + //~| ERROR expected a type, found a trait //~| ERROR associated item referring to unboxed trait object for its own trait - //~| ERROR the trait `C` cannot be made into an object } fn main() {} diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr index 53304382584..c0cfb18955c 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr @@ -11,22 +11,8 @@ help: you might have meant to use `Self` to refer to the implementing type LL | fn f(a: Self) -> Self; | ~~~~ ~~~~ -error[E0038]: the trait `A` cannot be made into an object - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 - | -LL | fn f(a: A) -> A; - | ^ `A` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:3:10 - | -LL | trait A: Sized { - | - ^^^^^ ...because it requires `Self: Sized` - | | - | this trait cannot be made into an object... - error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:11:13 + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13 | LL | trait B { | - in this trait @@ -38,30 +24,8 @@ help: you might have meant to use `Self` to refer to the implementing type LL | fn f(b: Self) -> Self; | ~~~~ ~~~~ -error[E0038]: the trait `B` cannot be made into an object - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:11:13 - | -LL | fn f(b: B) -> B; - | ^ `B` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:11:8 - | -LL | trait B { - | - this trait cannot be made into an object... -LL | fn f(b: B) -> B; - | ^ ...because associated function `f` has no `self` parameter -help: consider turning `f` into a method by giving it a `&self` argument - | -LL | fn f(&self, b: B) -> B; - | ++++++ -help: alternatively, consider constraining `f` so it does not apply to trait objects - | -LL | fn f(b: B) -> B where Self: Sized; - | +++++++++++++++++ - error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:18:20 + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20 | LL | trait C { | - in this trait @@ -73,23 +37,7 @@ help: you might have meant to use `Self` to refer to the implementing type LL | fn f(&self, c: Self) -> Self; | ~~~~ ~~~~ -error[E0038]: the trait `C` cannot be made into an object - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:18:20 - | -LL | fn f(&self, c: C) -> C; - | ----- ^ `C` cannot be made into an object - | | - | help: consider changing method `f`'s `self` parameter to be `&self` (notice the capitalization): `&Self` - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:18:10 - | -LL | trait C { - | - this trait cannot be made into an object... -LL | fn f(&self, c: C) -> C; - | ^^^^^ ...because method `f`'s `self` parameter cannot be dispatched on - -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 | LL | fn f(a: A) -> A; @@ -105,7 +53,7 @@ help: you can also use an opaque type, but users won't be able to specify the ty LL | fn f(a: impl A) -> A; | ++++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:19 | LL | fn f(a: A) -> A; @@ -116,8 +64,8 @@ help: `A` is dyn-incompatible, use `impl A` to return an opaque type, as long as LL | fn f(a: A) -> impl A; | ++++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:11:13 +error[E0782]: expected a type, found a trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13 | LL | fn f(b: B) -> B; | ^ @@ -132,8 +80,8 @@ help: you can also use an opaque type, but users won't be able to specify the ty LL | fn f(b: impl B) -> B; | ++++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:11:19 +error[E0782]: expected a type, found a trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:19 | LL | fn f(b: B) -> B; | ^ @@ -143,8 +91,8 @@ help: `B` is dyn-incompatible, use `impl B` to return an opaque type, as long as LL | fn f(b: B) -> impl B; | ++++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:18:20 +error[E0782]: expected a type, found a trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20 | LL | fn f(&self, c: C) -> C; | ^ @@ -159,8 +107,8 @@ help: you can also use an opaque type, but users won't be able to specify the ty LL | fn f(&self, c: impl C) -> C; | ++++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:18:26 +error[E0782]: expected a type, found a trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:26 | LL | fn f(&self, c: C) -> C; | ^ @@ -170,7 +118,6 @@ help: `C` is dyn-incompatible, use `impl C` to return an opaque type, as long as LL | fn f(&self, c: C) -> impl C; | ++++ -error: aborting due to 12 previous errors +error: aborting due to 9 previous errors -Some errors have detailed explanations: E0038, E0782. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/suggestions/issue-116434-2021.rs b/tests/ui/suggestions/issue-116434-2021.rs index 6feba3dc6c1..77cdfb8b614 100644 --- a/tests/ui/suggestions/issue-116434-2021.rs +++ b/tests/ui/suggestions/issue-116434-2021.rs @@ -3,7 +3,8 @@ trait Foo { type Clone; fn foo() -> Clone; - //~^ ERROR the trait `Clone` cannot be made into an object [E0038] + //~^ ERROR expected a type, found a trait + //~| HELP `Clone` is dyn-incompatible, use `impl Clone` to return an opaque type, as long as you return a single underlying type //~| HELP there is an associated type with the same name } @@ -12,7 +13,8 @@ trait DbHandle: Sized {} trait DbInterface { type DbHandle; fn handle() -> DbHandle; - //~^ ERROR the trait `DbHandle` cannot be made into an object [E0038] + //~^ ERROR expected a type, found a trait + //~| HELP `DbHandle` is dyn-incompatible, use `impl DbHandle` to return an opaque type, as long as you return a single underlying type //~| HELP there is an associated type with the same name } diff --git a/tests/ui/suggestions/issue-116434-2021.stderr b/tests/ui/suggestions/issue-116434-2021.stderr index 7f8cc147210..3853f03f1db 100644 --- a/tests/ui/suggestions/issue-116434-2021.stderr +++ b/tests/ui/suggestions/issue-116434-2021.stderr @@ -1,29 +1,28 @@ -error[E0038]: the trait `Clone` cannot be made into an object +error[E0782]: expected a type, found a trait --> $DIR/issue-116434-2021.rs:5:17 | LL | fn foo() -> Clone; - | ^^^^^ `Clone` cannot be made into an object + | ^^^^^ | - = note: the trait cannot be made into an object because it requires `Self: Sized` - = note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> +help: `Clone` is dyn-incompatible, use `impl Clone` to return an opaque type, as long as you return a single underlying type + | +LL | fn foo() -> impl Clone; + | ++++ help: there is an associated type with the same name | LL | fn foo() -> Self::Clone; | ++++++ -error[E0038]: the trait `DbHandle` cannot be made into an object - --> $DIR/issue-116434-2021.rs:14:20 +error[E0782]: expected a type, found a trait + --> $DIR/issue-116434-2021.rs:15:20 | LL | fn handle() -> DbHandle; - | ^^^^^^^^ `DbHandle` cannot be made into an object + | ^^^^^^^^ | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/issue-116434-2021.rs:10:17 +help: `DbHandle` is dyn-incompatible, use `impl DbHandle` to return an opaque type, as long as you return a single underlying type | -LL | trait DbHandle: Sized {} - | -------- ^^^^^ ...because it requires `Self: Sized` - | | - | this trait cannot be made into an object... +LL | fn handle() -> impl DbHandle; + | ++++ help: there is an associated type with the same name | LL | fn handle() -> Self::DbHandle; @@ -31,4 +30,4 @@ LL | fn handle() -> Self::DbHandle; error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/suggestions/suggest-blanket-impl-local-trait.rs b/tests/ui/suggestions/suggest-blanket-impl-local-trait.rs index 76300c6a3f4..dd7e203abd7 100644 --- a/tests/ui/suggestions/suggest-blanket-impl-local-trait.rs +++ b/tests/ui/suggestions/suggest-blanket-impl-local-trait.rs @@ -11,48 +11,48 @@ trait LocalTraitTwo { } trait GenericTrait<T> {} impl LocalTraitTwo for LocalTraitOne {} -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object //~| HELP alternatively use a blanket implementation to implement `LocalTraitTwo` for all types that also implement `LocalTraitOne` impl fmt::Display for LocalTraitOne { -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { todo!(); } } impl fmt::Display for LocalTraitTwo + Send { -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { todo!(); } } impl LocalTraitOne for fmt::Display {} -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object //~| HELP alternatively use a blanket implementation to implement `LocalTraitOne` for all types that also implement `fmt::Display` impl LocalTraitOne for fmt::Display + Send {} -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object //~| HELP alternatively use a blanket implementation to implement `LocalTraitOne` for all types that also implement `fmt::Display + Send` impl<E> GenericTrait<E> for LocalTraitOne {} -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object //~| HELP alternatively use a blanket implementation to implement `GenericTrait<E>` for all types that also implement `LocalTraitOne` trait GenericTraitTwo<T> {} impl<T, E> GenericTraitTwo<E> for GenericTrait<T> {} -//~^ ERROR trait objects must include the `dyn` keyword -//~| HELP add `dyn` keyword before this trait +//~^ ERROR expected a type, found a trait +//~| HELP you can add the `dyn` keyword if you want a trait object //~| HELP alternatively use a blanket implementation to implement `GenericTraitTwo<E>` for all types that also implement `GenericTrait<T>` fn main() {} diff --git a/tests/ui/suggestions/suggest-blanket-impl-local-trait.stderr b/tests/ui/suggestions/suggest-blanket-impl-local-trait.stderr index 769f3bd64f3..102438e1ec5 100644 --- a/tests/ui/suggestions/suggest-blanket-impl-local-trait.stderr +++ b/tests/ui/suggestions/suggest-blanket-impl-local-trait.stderr @@ -1,10 +1,10 @@ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-blanket-impl-local-trait.rs:34:24 | LL | impl LocalTraitOne for fmt::Display {} | ^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl LocalTraitOne for dyn fmt::Display {} | +++ @@ -13,13 +13,13 @@ help: alternatively use a blanket implementation to implement `LocalTraitOne` fo LL | impl<T: fmt::Display> LocalTraitOne for T {} | +++++++++++++++++ ~ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-blanket-impl-local-trait.rs:40:24 | LL | impl LocalTraitOne for fmt::Display + Send {} | ^^^^^^^^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl LocalTraitOne for dyn fmt::Display + Send {} | +++ @@ -28,13 +28,13 @@ help: alternatively use a blanket implementation to implement `LocalTraitOne` fo LL | impl<T: fmt::Display + Send> LocalTraitOne for T {} | ++++++++++++++++++++++++ ~ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-blanket-impl-local-trait.rs:13:24 | LL | impl LocalTraitTwo for LocalTraitOne {} | ^^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl LocalTraitTwo for dyn LocalTraitOne {} | +++ @@ -43,13 +43,13 @@ help: alternatively use a blanket implementation to implement `LocalTraitTwo` fo LL | impl<T: LocalTraitOne> LocalTraitTwo for T {} | ++++++++++++++++++ ~ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-blanket-impl-local-trait.rs:46:29 | LL | impl<E> GenericTrait<E> for LocalTraitOne {} | ^^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl<E> GenericTrait<E> for dyn LocalTraitOne {} | +++ @@ -58,35 +58,35 @@ help: alternatively use a blanket implementation to implement `GenericTrait<E>` LL | impl<E, T: LocalTraitOne> GenericTrait<E> for T {} | ++++++++++++++++++ ~ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-blanket-impl-local-trait.rs:18:23 | LL | impl fmt::Display for LocalTraitOne { | ^^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl fmt::Display for dyn LocalTraitOne { | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-blanket-impl-local-trait.rs:26:23 | LL | impl fmt::Display for LocalTraitTwo + Send { | ^^^^^^^^^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl fmt::Display for dyn LocalTraitTwo + Send { | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-blanket-impl-local-trait.rs:53:35 | LL | impl<T, E> GenericTraitTwo<E> for GenericTrait<T> {} | ^^^^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl<T, E> GenericTraitTwo<E> for dyn GenericTrait<T> {} | +++ diff --git a/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.rs b/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.rs index a5ab8be7f45..fe36d093430 100644 --- a/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.rs +++ b/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.rs @@ -14,14 +14,14 @@ pub union Union<T> { impl<'a, T> Struct<T> for Trait<'a, T> {} //~^ ERROR expected trait, found struct `Struct` -//~| ERROR trait objects must include the `dyn` keyword +//~| ERROR expected a type, found a trait impl<'a, T> Enum<T> for Trait<'a, T> {} //~^ ERROR expected trait, found enum `Enum` -//~| ERROR trait objects must include the `dyn` keyword +//~| ERROR expected a type, found a trait impl<'a, T> Union<T> for Trait<'a, T> {} //~^ ERROR expected trait, found union `Union` -//~| ERROR trait objects must include the `dyn` keyword +//~| ERROR expected a type, found a trait fn main() {} diff --git a/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr b/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr index 2893370570d..0bd601e2170 100644 --- a/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr +++ b/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr @@ -58,35 +58,35 @@ LL | pub union Union<T> { = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:15:27 | LL | impl<'a, T> Struct<T> for Trait<'a, T> {} | ^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl<'a, T> Struct<T> for dyn Trait<'a, T> {} | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:19:25 | LL | impl<'a, T> Enum<T> for Trait<'a, T> {} | ^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl<'a, T> Enum<T> for dyn Trait<'a, T> {} | +++ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:23:26 | LL | impl<'a, T> Union<T> for Trait<'a, T> {} | ^^^^^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl<'a, T> Union<T> for dyn Trait<'a, T> {} | +++ diff --git a/tests/ui/traits/bound/not-on-bare-trait-2021.rs b/tests/ui/traits/bound/not-on-bare-trait-2021.rs index 07b866cb4a6..93d2f04b54e 100644 --- a/tests/ui/traits/bound/not-on-bare-trait-2021.rs +++ b/tests/ui/traits/bound/not-on-bare-trait-2021.rs @@ -6,13 +6,11 @@ trait Foo { // This should emit the less confusing error, not the more confusing one. fn foo(_x: Foo + Send) { - //~^ ERROR trait objects must include the `dyn` keyword - //~| ERROR size for values of type + //~^ ERROR expected a type, found a trait } fn bar(x: Foo) -> Foo { - //~^ ERROR trait objects must include the `dyn` keyword - //~| ERROR trait objects must include the `dyn` keyword - //~| ERROR size for values of type + //~^ ERROR expected a type, found a trait + //~| ERROR expected a type, found a trait x } diff --git a/tests/ui/traits/bound/not-on-bare-trait-2021.stderr b/tests/ui/traits/bound/not-on-bare-trait-2021.stderr index e05ae8e5267..e50186aff7e 100644 --- a/tests/ui/traits/bound/not-on-bare-trait-2021.stderr +++ b/tests/ui/traits/bound/not-on-bare-trait-2021.stderr @@ -1,38 +1,4 @@ -error[E0277]: the size for values of type `(dyn Foo + Send + 'static)` cannot be known at compilation time - --> $DIR/not-on-bare-trait-2021.rs:8:12 - | -LL | fn foo(_x: Foo + Send) { - | ^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `(dyn Foo + Send + 'static)` - = help: unsized fn params are gated as an unstable feature -help: you can use `impl Trait` as the argument type - | -LL | fn foo(_x: impl Foo + Send) { - | ++++ -help: function arguments must have a statically known size, borrowed types always have a known size - | -LL | fn foo(_x: &(dyn Foo + Send)) { - | +++++ + - -error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known at compilation time - --> $DIR/not-on-bare-trait-2021.rs:12:11 - | -LL | fn bar(x: Foo) -> Foo { - | ^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `(dyn Foo + 'static)` - = help: unsized fn params are gated as an unstable feature -help: you can use `impl Trait` as the argument type - | -LL | fn bar(x: impl Foo) -> Foo { - | ++++ -help: function arguments must have a statically known size, borrowed types always have a known size - | -LL | fn bar(x: &dyn Foo) -> Foo { - | ++++ - -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/not-on-bare-trait-2021.rs:8:12 | LL | fn foo(_x: Foo + Send) { @@ -51,8 +17,8 @@ help: alternatively, use a trait object to accept any type that implements `Foo LL | fn foo(_x: &(dyn Foo + Send)) { | +++++ + -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/not-on-bare-trait-2021.rs:12:11 +error[E0782]: expected a type, found a trait + --> $DIR/not-on-bare-trait-2021.rs:11:11 | LL | fn bar(x: Foo) -> Foo { | ^^^ @@ -70,8 +36,8 @@ help: alternatively, use a trait object to accept any type that implements `Foo` LL | fn bar(x: &dyn Foo) -> Foo { | ++++ -error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/not-on-bare-trait-2021.rs:12:19 +error[E0782]: expected a type, found a trait + --> $DIR/not-on-bare-trait-2021.rs:11:19 | LL | fn bar(x: Foo) -> Foo { | ^^^ @@ -85,7 +51,6 @@ help: alternatively, you can return an owned trait object LL | fn bar(x: Foo) -> Box<dyn Foo> { | +++++++ + -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0277, E0782. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/traits/issue-106072.rs b/tests/ui/traits/issue-106072.rs index 8adbac46a5b..696bd765ebc 100644 --- a/tests/ui/traits/issue-106072.rs +++ b/tests/ui/traits/issue-106072.rs @@ -1,6 +1,4 @@ -#[derive(Clone)] //~ trait objects must include the `dyn` keyword -//~^ ERROR: the size for values of type `(dyn Foo + 'static)` cannot be known -//~| ERROR: return type cannot have an unboxed trait object +#[derive(Clone)] //~ expected a type, found a trait struct Foo; trait Foo {} //~ the name `Foo` is defined multiple times fn main() {} diff --git a/tests/ui/traits/issue-106072.stderr b/tests/ui/traits/issue-106072.stderr index 6476c8b3237..4a48e4e898d 100644 --- a/tests/ui/traits/issue-106072.stderr +++ b/tests/ui/traits/issue-106072.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `Foo` is defined multiple times - --> $DIR/issue-106072.rs:5:1 + --> $DIR/issue-106072.rs:3:1 | LL | struct Foo; | ----------- previous definition of the type `Foo` here @@ -8,26 +8,7 @@ LL | trait Foo {} | = note: `Foo` must be defined only once in the type namespace of this module -error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known at compilation time - --> $DIR/issue-106072.rs:1:10 - | -LL | #[derive(Clone)] - | ^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `(dyn Foo + 'static)` -note: required by a bound in `Clone` - --> $SRC_DIR/core/src/clone.rs:LL:COL - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0746]: return type cannot have an unboxed trait object - --> $DIR/issue-106072.rs:1:10 - | -LL | #[derive(Clone)] - | ^^^^^ doesn't have a size known at compile-time - | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/issue-106072.rs:1:10 | LL | #[derive(Clone)] @@ -35,7 +16,7 @@ LL | #[derive(Clone)] | = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0277, E0428, E0746, E0782. -For more information about an error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0428, E0782. +For more information about an error, try `rustc --explain E0428`. diff --git a/tests/ui/traits/missing-for-type-in-impl.e2021.stderr b/tests/ui/traits/missing-for-type-in-impl.e2021.stderr index b5a607a54cb..a49c5d9d45b 100644 --- a/tests/ui/traits/missing-for-type-in-impl.e2021.stderr +++ b/tests/ui/traits/missing-for-type-in-impl.e2021.stderr @@ -10,13 +10,13 @@ help: this trait has no implementations, consider adding one LL | trait Foo<T> { | ^^^^^^^^^^^^ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> $DIR/missing-for-type-in-impl.rs:8:6 | LL | impl Foo<i64> { | ^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | LL | impl dyn Foo<i64> { | +++ diff --git a/tests/ui/traits/missing-for-type-in-impl.rs b/tests/ui/traits/missing-for-type-in-impl.rs index 7d4ad479e77..e5dd3651609 100644 --- a/tests/ui/traits/missing-for-type-in-impl.rs +++ b/tests/ui/traits/missing-for-type-in-impl.rs @@ -6,7 +6,7 @@ trait Foo<T> { /* note the "missing" for ... (in this case for i64, in order for this to compile) */ impl Foo<i64> { -//[e2021]~^ ERROR trait objects must include the `dyn` keyword +//[e2021]~^ ERROR expected a type, found a trait //[e2015]~^^ WARNING trait objects without an explicit `dyn` are deprecated //[e2015]~| WARNING trait objects without an explicit `dyn` are deprecated //[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! diff --git a/tests/ui/use/unused-trait-with-method-err.rs b/tests/ui/use/unused-trait-with-method-err.rs new file mode 100644 index 00000000000..37684e1bf81 --- /dev/null +++ b/tests/ui/use/unused-trait-with-method-err.rs @@ -0,0 +1,17 @@ +// Test that we don't issue an unused import warning when there's +// a method lookup error and that trait was possibly applicable. + +use foo::Bar; + +mod foo { + pub trait Bar { + fn uwu(&self) {} + } +} + +struct Foo; + +fn main() { + Foo.uwu(); + //~^ ERROR no method named `uwu` found for struct `Foo` in the current scope +} diff --git a/tests/ui/use/unused-trait-with-method-err.stderr b/tests/ui/use/unused-trait-with-method-err.stderr new file mode 100644 index 00000000000..7ca4563673b --- /dev/null +++ b/tests/ui/use/unused-trait-with-method-err.stderr @@ -0,0 +1,19 @@ +error[E0599]: no method named `uwu` found for struct `Foo` in the current scope + --> $DIR/unused-trait-with-method-err.rs:15:9 + | +LL | struct Foo; + | ---------- method `uwu` not found for this struct +... +LL | Foo.uwu(); + | ^^^ method not found in `Foo` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Bar` defines an item `uwu`, perhaps you need to implement it + --> $DIR/unused-trait-with-method-err.rs:7:5 + | +LL | pub trait Bar { + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/triagebot.toml b/triagebot.toml index 60ec1043aad..33dcbfa55a4 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -431,6 +431,11 @@ trigger_files = [ "src/tools/run-make-support" ] +[autolabel."A-compiletest"] +trigger_files = [ + "src/tools/compiletest" +] + [notify-zulip."I-prioritize"] zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts topic = "#{number} {title}" |
