From 331abe2d1fe176f3338eb1ff25a719f3c389cd5e Mon Sep 17 00:00:00 2001 From: Paul Jewell Date: Fri, 17 Nov 2023 14:41:34 +0000 Subject: [PATCH 001/257] Updated instructions for installing rust-analyzer under Gentoo. --- docs/user/manual.adoc | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 18d5ddd4d0af..1d8b5ac3bcc7 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -186,18 +186,7 @@ $ pacman -S rust-analyzer ==== Gentoo Linux -`rust-analyzer` is available in the GURU repository: - -- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer?id=9895cea62602cfe599bd48e0fb02127411ca6e81[`dev-util/rust-analyzer`] builds from source -- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer-bin?id=9895cea62602cfe599bd48e0fb02127411ca6e81[`dev-util/rust-analyzer-bin`] installs an official binary release - -If not already, GURU must be enabled (e.g. using `app-eselect/eselect-repository`) and sync'd before running `emerge`: - -[source,bash] ----- -$ eselect repository enable guru && emaint sync -r guru -$ emerge rust-analyzer-bin ----- +`rust-analyzer` is installed when the `rust-analyzer` use flag is set for dev-lang/rust or dev-lang/rust-bin. You also need to set the `rust-src` use flag. ==== macOS From 4a8ba05a8361b5db3d8fd0907efd993be4d4d5b5 Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Thu, 9 Nov 2023 17:24:19 +0100 Subject: [PATCH 002/257] Making `User` and `User<[T]>` `Send` --- library/std/src/sys/sgx/abi/usercalls/alloc.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs index 817c33b66037..f99cea360f1f 100644 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs @@ -185,6 +185,12 @@ pub struct UserRef(UnsafeCell); #[unstable(feature = "sgx_platform", issue = "56975")] pub struct User(NonNull>); +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl Send for User {} + +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl Send for User<[T]> {} + trait NewUserRef { unsafe fn new_userref(v: T) -> Self; } From a2b79cd92f41fd83f08194b43b034f9e227a33bb Mon Sep 17 00:00:00 2001 From: Patryk Wychowaniec Date: Tue, 5 Dec 2023 15:58:07 +0100 Subject: [PATCH 003/257] chore: Bump compiler_builtins Actually closes https://github.com/rust-lang/rust/issues/118079. --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1b5ea30f504..cb9fcde8854d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,9 +704,9 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" [[package]] name = "compiler_builtins" -version = "0.1.103" +version = "0.1.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b73c3443a5fd2438d7ba4853c64e4c8efc2404a9e28a9234cc2d5eebc6c242" +checksum = "99c3f9035afc33f4358773239573f7d121099856753e1bbd2a6a5207098fc741" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index f666b18887cb..3f889f668c74 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -18,7 +18,7 @@ panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } libc = { version = "0.2.150", default-features = false, features = ['rustc-dep-of-std'], public = true } -compiler_builtins = { version = "0.1.103" } +compiler_builtins = { version = "0.1.104" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] } @@ -49,8 +49,8 @@ hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] -r-efi = { version = "4.2.0", features = ['rustc-dep-of-std']} -r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std']} +r-efi = { version = "4.2.0", features = ['rustc-dep-of-std'] } +r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } [features] backtrace = [ From bc6a5c71b635a9a5ea52c0b5f4ff297b0f611ac7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 8 Dec 2023 15:26:16 +0000 Subject: [PATCH 004/257] std: getrandom simplification for freebsd. it is in the libcs' crate too now. --- library/std/src/sys/unix/rand.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index 2825d1677427..cf0fe0f47c53 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -64,17 +64,7 @@ mod imp { #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd"))] fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - #[cfg(not(target_os = "freebsd"))] - use libc::getrandom; - #[cfg(target_os = "freebsd")] - extern "C" { - fn getrandom( - buf: *mut libc::c_void, - buflen: libc::size_t, - flags: libc::c_uint, - ) -> libc::ssize_t; - } - unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } + unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } } #[cfg(not(any( From af4913fcf4a23ff6f4ddd68a6b0266629105bddb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Dec 2023 14:36:34 +0100 Subject: [PATCH 005/257] merge core_panic feature into panic_internals --- library/alloc/src/lib.rs | 2 +- library/core/src/lib.rs | 2 +- library/core/src/macros/mod.rs | 12 ++++++------ library/core/src/panic.rs | 14 +++++++------- library/core/src/panicking.rs | 18 +++++++++--------- library/std/src/lib.rs | 1 - library/std/src/panic.rs | 2 +- .../src/library-features/core-panic.md | 5 ----- ...allow-unwind-when-calling-panic-directly.rs | 2 +- 9 files changed, 26 insertions(+), 32 deletions(-) delete mode 100644 src/doc/unstable-book/src/library-features/core-panic.md diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 0af3ac38ee53..493525534867 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -120,7 +120,6 @@ #![feature(const_size_of_val)] #![feature(const_waker)] #![feature(core_intrinsics)] -#![feature(core_panic)] #![feature(deprecated_suggestion)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] @@ -139,6 +138,7 @@ #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_uninit_array_transpose)] +#![feature(panic_internals)] #![feature(pattern)] #![feature(ptr_internals)] #![feature(ptr_metadata)] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 07720f235989..5b0a91337718 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -168,7 +168,6 @@ #![feature(const_unicode_case_lookup)] #![feature(const_unsafecell_get_mut)] #![feature(const_waker)] -#![feature(core_panic)] #![feature(coverage_attribute)] #![feature(duration_consts_float)] #![feature(internal_impls_macro)] @@ -180,6 +179,7 @@ #![feature(non_null_convenience)] #![feature(offset_of)] #![feature(offset_of_enum)] +#![feature(panic_internals)] #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 7f5908e477cf..a2437feeeb9c 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -32,7 +32,7 @@ macro_rules! panic { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "assert_eq_macro")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(panic_internals)] macro_rules! assert_eq { ($left:expr, $right:expr $(,)?) => { match (&$left, &$right) { @@ -82,7 +82,7 @@ macro_rules! assert_eq { #[macro_export] #[stable(feature = "assert_ne", since = "1.13.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "assert_ne_macro")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(panic_internals)] macro_rules! assert_ne { ($left:expr, $right:expr $(,)?) => { match (&$left, &$right) { @@ -139,7 +139,7 @@ macro_rules! assert_ne { /// assert_matches!(c, Ok(x) | Err(x) if x.len() < 100); /// ``` #[unstable(feature = "assert_matches", issue = "82775")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(panic_internals)] #[rustc_macro_transparency = "semitransparent"] pub macro assert_matches { ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => { @@ -787,7 +787,7 @@ macro_rules! unreachable { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "unimplemented_macro")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(panic_internals)] macro_rules! unimplemented { () => { $crate::panicking::panic("not implemented") @@ -867,7 +867,7 @@ macro_rules! unimplemented { #[macro_export] #[stable(feature = "todo_macro", since = "1.40.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "todo_macro")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(panic_internals)] macro_rules! todo { () => { $crate::panicking::panic("not yet implemented") @@ -1534,7 +1534,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] #[rustc_diagnostic_item = "assert_macro"] - #[allow_internal_unstable(core_panic, edition_panic, generic_assert_internals)] + #[allow_internal_unstable(panic_internals, edition_panic, generic_assert_internals)] macro_rules! assert { ($cond:expr $(,)?) => {{ /* compiler built-in */ }}; ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }}; diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 4ca5af1eaea3..380933ce1960 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -17,7 +17,7 @@ pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] -#[allow_internal_unstable(core_panic, const_format_args)] +#[allow_internal_unstable(panic_internals, const_format_args)] #[rustc_diagnostic_item = "core_panic_2015_macro"] #[rustc_macro_transparency = "semitransparent"] pub macro panic_2015 { @@ -44,7 +44,7 @@ pub macro panic_2015 { #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] -#[allow_internal_unstable(core_panic, const_format_args)] +#[allow_internal_unstable(panic_internals, const_format_args)] #[rustc_diagnostic_item = "core_panic_2021_macro"] #[rustc_macro_transparency = "semitransparent"] #[cfg(feature = "panic_immediate_abort")] @@ -66,7 +66,7 @@ pub macro panic_2021 { #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] #[allow_internal_unstable( - core_panic, + panic_internals, core_intrinsics, const_dispatch, const_eval_select, @@ -109,7 +109,7 @@ pub macro panic_2021 { #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(panic_internals)] #[rustc_diagnostic_item = "unreachable_2015_macro"] #[rustc_macro_transparency = "semitransparent"] pub macro unreachable_2015 { @@ -128,7 +128,7 @@ pub macro unreachable_2015 { #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(panic_internals)] #[rustc_macro_transparency = "semitransparent"] pub macro unreachable_2021 { () => ( @@ -145,8 +145,8 @@ pub macro unreachable_2021 { /// unwind. For example, checks in `_unchecked` functions that are intended for debugging but should /// not compromise unwind safety. #[doc(hidden)] -#[unstable(feature = "core_panic", issue = "none")] -#[allow_internal_unstable(core_panic, const_format_args)] +#[unstable(feature = "panic_internals", issue = "none")] +#[allow_internal_unstable(panic_internals, const_format_args)] #[rustc_macro_transparency = "semitransparent"] pub macro debug_assert_nounwind { ($cond:expr $(,)?) => { diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 1b6e77b96b1d..0819334b600b 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -21,7 +21,7 @@ #![allow(dead_code, missing_docs)] #![unstable( - feature = "core_panic", + feature = "panic_internals", reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] @@ -48,7 +48,7 @@ const _: () = assert!(cfg!(panic = "abort"), "panic_immediate_abort requires -C #[track_caller] #[lang = "panic_fmt"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval -#[rustc_const_unstable(feature = "core_panic", issue = "none")] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { super::intrinsics::abort() @@ -82,7 +82,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { // and unwinds anyway, we will hit the "unwinding out of nounwind function" guard, // which causes a "panic in a function that cannot unwind". #[rustc_nounwind] -#[rustc_const_unstable(feature = "core_panic", issue = "none")] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! { #[inline] // this should always be inlined into `panic_nounwind_fmt` #[track_caller] @@ -132,7 +132,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] -#[rustc_const_unstable(feature = "core_panic", issue = "none")] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] #[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators pub const fn panic(expr: &'static str) -> ! { // Use Arguments::new_v1 instead of format_args!("{expr}") to potentially @@ -150,7 +150,7 @@ pub const fn panic(expr: &'static str) -> ! { #[cfg_attr(feature = "panic_immediate_abort", inline)] #[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics #[rustc_nounwind] -#[rustc_const_unstable(feature = "core_panic", issue = "none")] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] pub const fn panic_nounwind(expr: &'static str) -> ! { panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ false); } @@ -166,7 +166,7 @@ pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { #[inline] #[track_caller] #[rustc_diagnostic_item = "panic_str"] -#[rustc_const_unstable(feature = "core_panic", issue = "none")] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] pub const fn panic_str(expr: &str) -> ! { panic_display(&expr); } @@ -174,7 +174,7 @@ pub const fn panic_str(expr: &str) -> ! { #[track_caller] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[rustc_const_unstable(feature = "core_panic", issue = "none")] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] pub const fn panic_explicit() -> ! { panic_display(&"explicit panic"); } @@ -191,7 +191,7 @@ pub fn unreachable_display(x: &T) -> ! { #[rustc_do_not_const_check] // hooked by const-eval // enforce a &&str argument in const-check and hook this by const-eval #[rustc_const_panic_str] -#[rustc_const_unstable(feature = "core_panic", issue = "none")] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] pub const fn panic_display(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); } @@ -258,7 +258,7 @@ fn panic_in_cleanup() -> ! { /// This function is used instead of panic_fmt in const eval. #[lang = "const_panic_fmt"] -#[rustc_const_unstable(feature = "core_panic", issue = "none")] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if let Some(msg) = fmt.as_str() { // The panic_display function is hooked by const eval. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 52b1fe822d6c..9f7513c0d5dd 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -375,7 +375,6 @@ #![feature(cfg_eval)] #![feature(concat_bytes)] #![feature(const_format_args)] -#![feature(core_panic)] #![feature(custom_test_frameworks)] #![feature(edition_panic)] #![feature(format_args_nl)] diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 69a6f3e6d5ac..7f6b563d7295 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -11,7 +11,7 @@ use crate::thread::Result; #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] -#[allow_internal_unstable(libstd_sys_internals, const_format_args, core_panic, rt)] +#[allow_internal_unstable(libstd_sys_internals, const_format_args, panic_internals, rt)] #[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")] #[rustc_macro_transparency = "semitransparent"] pub macro panic_2015 { diff --git a/src/doc/unstable-book/src/library-features/core-panic.md b/src/doc/unstable-book/src/library-features/core-panic.md deleted file mode 100644 index c197588404c9..000000000000 --- a/src/doc/unstable-book/src/library-features/core-panic.md +++ /dev/null @@ -1,5 +0,0 @@ -# `core_panic` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs b/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs index 233120c92f38..24fc512dfbf0 100644 --- a/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs +++ b/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs @@ -22,7 +22,7 @@ //[thin]compile-flags: -C lto=thin //[fat]compile-flags: -C lto=fat -#![feature(core_panic)] +#![feature(panic_internals)] // (For some reason, reproducing the LTO issue requires pulling in std // explicitly this way.) From 707c4f967e4d6c68252eb784844a10a3613bde61 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 15 Dec 2023 06:02:40 +0000 Subject: [PATCH 006/257] unify query canonicalization mode --- .../src/infer/canonical/canonicalizer.rs | 83 ++++++------------- .../src/traits/outlives_bounds.rs | 3 +- .../src/traits/query/evaluate_obligation.rs | 6 +- .../src/traits/query/normalize.rs | 5 +- .../src/traits/query/type_op/mod.rs | 7 +- .../hrtb-cache-issue-54302.stderr | 2 +- .../trait-bounds/issue-59311.stderr | 2 +- tests/ui/nll/issue-54302.stderr | 2 +- 8 files changed, 35 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 5b00ef42211e..fb703f385709 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -41,7 +41,33 @@ impl<'tcx> InferCtxt<'tcx> { where V: TypeFoldable>, { - self.canonicalize_query_with_mode(value, query_state, &CanonicalizeAllFreeRegions) + let (param_env, value) = value.into_parts(); + let param_env = self.tcx.canonical_param_env_cache.get_or_insert( + self.tcx, + param_env, + query_state, + |tcx, param_env, query_state| { + // FIXME(#118965): We don't canonicalize the static lifetimes that appear in the + // `param_env` beacause they are treated differently by trait selection. + Canonicalizer::canonicalize( + param_env, + None, + tcx, + &CanonicalizeFreeRegionsOtherThanStatic, + query_state, + ) + }, + ); + + Canonicalizer::canonicalize_with_base( + param_env, + value, + Some(self), + self.tcx, + &CanonicalizeAllFreeRegions, + query_state, + ) + .unchecked_map(|(param_env, value)| param_env.and(value)) } /// Canonicalizes a query *response* `V`. When we canonicalize a @@ -96,61 +122,6 @@ impl<'tcx> InferCtxt<'tcx> { &mut query_state, ) } - - /// A variant of `canonicalize_query` that does not - /// canonicalize `'static`. This is useful when - /// the query implementation can perform more efficient - /// handling of `'static` regions (e.g. trait evaluation). - pub fn canonicalize_query_keep_static( - &self, - value: ty::ParamEnvAnd<'tcx, V>, - query_state: &mut OriginalQueryValues<'tcx>, - ) -> Canonical<'tcx, ty::ParamEnvAnd<'tcx, V>> - where - V: TypeFoldable>, - { - self.canonicalize_query_with_mode( - value, - query_state, - &CanonicalizeFreeRegionsOtherThanStatic, - ) - } - - fn canonicalize_query_with_mode( - &self, - value: ty::ParamEnvAnd<'tcx, V>, - query_state: &mut OriginalQueryValues<'tcx>, - canonicalize_region_mode: &dyn CanonicalizeMode, - ) -> Canonical<'tcx, ty::ParamEnvAnd<'tcx, V>> - where - V: TypeFoldable>, - { - let (param_env, value) = value.into_parts(); - let base = self.tcx.canonical_param_env_cache.get_or_insert( - self.tcx, - param_env, - query_state, - |tcx, param_env, query_state| { - Canonicalizer::canonicalize( - param_env, - None, - tcx, - &CanonicalizeFreeRegionsOtherThanStatic, - query_state, - ) - }, - ); - - Canonicalizer::canonicalize_with_base( - base, - value, - Some(self), - self.tcx, - canonicalize_region_mode, - query_state, - ) - .unchecked_map(|(param_env, value)| param_env.and(value)) - } } /// Controls how we canonicalize "free regions" that are not inference diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 7513f88cfc3c..3b5a25814d38 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -65,8 +65,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { assert!(!ty.has_non_region_infer()); let mut canonical_var_values = OriginalQueryValues::default(); - let canonical_ty = - self.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values); + let canonical_ty = self.canonicalize_query(param_env.and(ty), &mut canonical_var_values); let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else { return vec![]; }; diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index d812d537d8c1..31e34096fb00 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -87,10 +87,8 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { }) } else { assert!(!self.intercrate); - let c_pred = self.canonicalize_query_keep_static( - param_env.and(obligation.predicate), - &mut _orig_values, - ); + let c_pred = + self.canonicalize_query(param_env.and(obligation.predicate), &mut _orig_values); self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 4728fcf3301c..b0364ac4ca50 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -275,10 +275,7 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> let data = data.try_fold_with(self)?; let mut orig_values = OriginalQueryValues::default(); - // HACK(matthewjasper) `'static` is special-cased in selection, - // so we cannot canonicalize it. - let c_data = infcx - .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values); + let c_data = infcx.canonicalize_query(self.param_env.and(data), &mut orig_values); debug!("QueryNormalizer: c_data = {:#?}", c_data); debug!("QueryNormalizer: orig_values = {:#?}", orig_values); let result = match kind { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 272f1a54f819..02024ede7eed 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -111,14 +111,9 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't return Ok((result, None, vec![], Certainty::Proven)); } - // FIXME(#33684) -- We need to use - // `canonicalize_query_keep_static` here because of things - // like the subtype query, which go awry around - // `'static` otherwise. let mut canonical_var_values = OriginalQueryValues::default(); let old_param_env = query_key.param_env; - let canonical_self = - infcx.canonicalize_query_keep_static(query_key, &mut canonical_var_values); + let canonical_self = infcx.canonicalize_query(query_key, &mut canonical_var_values); let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; let InferOk { value, obligations } = infcx diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-cache-issue-54302.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-cache-issue-54302.stderr index 7f81ee331bf6..186af160d908 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-cache-issue-54302.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-cache-issue-54302.stderr @@ -4,7 +4,7 @@ error: implementation of `Deserialize` is not general enough LL | assert_deserialize_owned::<&'static str>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Deserialize` is not general enough | - = note: `&'static str` must implement `Deserialize<'0>`, for any lifetime `'0`... + = note: `&str` must implement `Deserialize<'0>`, for any lifetime `'0`... = note: ...but `&str` actually implements `Deserialize<'1>`, for some specific lifetime `'1` error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr index 28c259be35f6..e50aad876d8c 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr +++ b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr @@ -12,7 +12,7 @@ error: higher-ranked lifetime error LL | v.t(|| {}); | ^^^^^ | - = note: could not prove `for<'a> &'a V: 'static` + = note: could not prove `for<'a> &'a V: 'b` error: aborting due to 2 previous errors diff --git a/tests/ui/nll/issue-54302.stderr b/tests/ui/nll/issue-54302.stderr index 269739af6026..a1e2d4014290 100644 --- a/tests/ui/nll/issue-54302.stderr +++ b/tests/ui/nll/issue-54302.stderr @@ -4,7 +4,7 @@ error: implementation of `Deserialize` is not general enough LL | assert_deserialize_owned::<&'static str>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Deserialize` is not general enough | - = note: `&'static str` must implement `Deserialize<'0>`, for any lifetime `'0`... + = note: `&str` must implement `Deserialize<'0>`, for any lifetime `'0`... = note: ...but `&str` actually implements `Deserialize<'1>`, for some specific lifetime `'1` error: aborting due to 1 previous error From ab716d0635b7eb0a829f79ac394f1c7a0a05a64a Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Fri, 15 Dec 2023 13:04:37 +0000 Subject: [PATCH 007/257] Use assert_unsafe_precondition for char::from_u32_unchecked Co-Authored-By: joboet --- library/core/src/char/convert.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 453de9754be5..177f39b59aed 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -4,6 +4,7 @@ use crate::char::TryFromCharError; use crate::convert::TryFrom; use crate::error::Error; use crate::fmt; +use crate::intrinsics::assert_unsafe_precondition; use crate::mem::transmute; use crate::str::FromStr; @@ -23,7 +24,13 @@ pub(super) const fn from_u32(i: u32) -> Option { #[must_use] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. - if cfg!(debug_assertions) { char::from_u32(i).unwrap() } else { unsafe { transmute(i) } } + unsafe { + assert_unsafe_precondition!( + "invalid value for `char`", + (i: u32) => char_try_from_u32(i).is_ok() + ); + transmute(i) + } } #[stable(feature = "char_convert", since = "1.13.0")] From 8a0a3b24934496741c5be376257e5108da0f29b5 Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Tue, 26 Dec 2023 21:23:41 +0800 Subject: [PATCH 008/257] fix: extract_struct_from_enum_variant should resolve `Self` generic arg --- .../extract_struct_from_enum_variant.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 37db27a8fc02..b55344f157d6 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -246,6 +246,32 @@ fn create_struct_def( Either::Left(field_list) => { let field_list = field_list.clone_for_update(); + // replace `Self` with the enum name when construct struct def + field_list + .fields() + .filter_map(|field| match field.ty()? { + ast::Type::PathType(p) => { + let generic_arg_list = p.path()?.segment()?.generic_arg_list()?; + Some( + generic_arg_list + .generic_args() + .filter_map(|generic_arg| { + if generic_arg.to_string() == "Self" { + let type_arg = + make::type_arg(make::ty(&enum_.name()?.to_string())) + .clone_for_update(); + Some(ted::replace(generic_arg.syntax(), type_arg.syntax())) + } else { + None + } + }) + .count(), + ) + } + _ => None, + }) + .count(); + if let Some(vis) = &enum_vis { field_list .fields() From 67f001e5ec9a48804efc1528e8b303c440846093 Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Tue, 26 Dec 2023 21:25:30 +0800 Subject: [PATCH 009/257] test: add test case for `Self` --- .../extract_struct_from_enum_variant.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index b55344f157d6..2e0a628eee7c 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -451,6 +451,27 @@ mod tests { use super::*; + #[test] + fn issue_16197() { + check_assist( + extract_struct_from_enum_variant, + r#" +enum Foo { + Bar $0{ node: Box }, + Nil, +} +"#, + r#" +struct Bar{ node: Box } + +enum Foo { + Bar(Bar), + Nil, +} +"#, + ); + } + #[test] fn test_extract_struct_several_fields_tuple() { check_assist( From f6a045cc6b24dd033c207dd63d8adccdedf672d2 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 29 Sep 2023 12:52:30 -0700 Subject: [PATCH 010/257] rustdoc: search for tuples and unit by type with `()` --- .../rustdoc/src/read-documentation/search.md | 67 +++- src/librustdoc/html/render/search_index.rs | 3 + src/librustdoc/html/static/js/search.js | 100 +++-- tests/rustdoc-js-std/parser-errors.js | 17 +- tests/rustdoc-js-std/parser-slice-array.js | 18 + tests/rustdoc-js-std/parser-tuple.js | 365 ++++++++++++++++++ tests/rustdoc-js-std/parser-weird-queries.js | 2 +- tests/rustdoc-js/tuple-unit.js | 80 ++++ tests/rustdoc-js/tuple-unit.rs | 18 + 9 files changed, 616 insertions(+), 54 deletions(-) create mode 100644 tests/rustdoc-js-std/parser-tuple.js create mode 100644 tests/rustdoc-js/tuple-unit.js create mode 100644 tests/rustdoc-js/tuple-unit.rs diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index 1f45bd6c6b82..d773794504e7 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -150,12 +150,55 @@ will match these queries: But it *does not* match `Result` or `Result>`. -Function signature searches also support arrays and slices. The explicit name -`primitive:slice` and `primitive:array` can be used to match a slice -or array of bytes, while square brackets `[u8]` will match either one. Empty -square brackets, `[]`, will match any slice or array regardless of what -it contains, while a slice with a type parameter, like `[T]`, will only match -functions that actually operate on generic slices. +### Primitives with Special Syntax + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ShorthandExplicit names
[]primitive:slice and/or primitive:array
[T]primitive:slice<T> and/or primitive:array<T>
()primitive:unit and/or primitive:tuple
(T)T
(T,)primitive:tuple<T>
!primitive:never
+ +When searching for `[]`, Rustdoc will return search results with either slices +or arrays. If you know which one you want, you can force it to return results +for `primitive:slice` or `primitive:array` using the explicit name syntax. +Empty square brackets, `[]`, will match any slice or array regardless of what +it contains, or an item type can be provided, such as `[u8]` or `[T]`, to +explicitly find functions that operate on byte slices or generic slices, +respectively. + +A single type expression wrapped in parens is the same as that type expression, +since parens act as the grouping operator. If they're empty, though, they will +match both `unit` and `tuple`, and if there's more than one type (or a trailing +or leading comma) it is the same as `primitive:tuple<...>`. ### Limitations and quirks of type-based search @@ -188,11 +231,10 @@ Most of these limitations should be addressed in future version of Rustdoc. that you don't want a type parameter, you can force it to match something else by giving it a different prefix like `struct:T`. - * It's impossible to search for references, pointers, or tuples. The + * It's impossible to search for references or pointers. The wrapped types can be searched for, so a function that takes `&File` can be found with `File`, but you'll get a parse error when typing an `&` - into the search field. Similarly, `Option<(T, U)>` can be matched with - `Option`, but `(` will give a parse error. + into the search field. * Searching for lifetimes is not supported. @@ -216,8 +258,9 @@ Item filters can be used in both name-based and type signature-based searches. ```text ident = *(ALPHA / DIGIT / "_") path = ident *(DOUBLE-COLON ident) [!] -slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET -arg = [type-filter *WS COLON *WS] (path [generics] / slice / [!]) +slice-like = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET +tuple-like = OPEN-PAREN [ nonempty-arg-list ] CLOSE-PAREN +arg = [type-filter *WS COLON *WS] (path [generics] / slice-like / tuple-like / [!]) type-sep = COMMA/WS *(COMMA/WS) nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) generic-arg-list = *(type-sep) arg [ EQUAL arg ] *(type-sep arg [ EQUAL arg ]) *(type-sep) @@ -263,6 +306,8 @@ OPEN-ANGLE-BRACKET = "<" CLOSE-ANGLE-BRACKET = ">" OPEN-SQUARE-BRACKET = "[" CLOSE-SQUARE-BRACKET = "]" +OPEN-PAREN = "(" +CLOSE-PAREN = ")" COLON = ":" DOUBLE-COLON = "::" QUOTE = %x22 diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index a1029320d2d2..6c6a31bbb112 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -581,6 +581,9 @@ fn get_index_type_id( // The type parameters are converted to generics in `simplify_fn_type` clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)), clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)), + clean::Tuple(ref n) if n.is_empty() => { + Some(RenderTypeId::Primitive(clean::PrimitiveType::Unit)) + } clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)), clean::QPath(ref data) => { if data.self_type.is_self_type() diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index e824a1fd4bda..b2263e9e3e1a 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -260,6 +260,18 @@ function initSearch(rawSearchIndex) { * Special type name IDs for searching by both array and slice (`[]` syntax). */ let typeNameIdOfArrayOrSlice; + /** + * Special type name IDs for searching by tuple. + */ + let typeNameIdOfTuple; + /** + * Special type name IDs for searching by unit. + */ + let typeNameIdOfUnit; + /** + * Special type name IDs for searching by both tuple and unit (`()` syntax). + */ + let typeNameIdOfTupleOrUnit; /** * Add an item to the type Name->ID map, or, if one already exists, use it. @@ -295,11 +307,7 @@ function initSearch(rawSearchIndex) { } function isEndCharacter(c) { - return "=,>-]".indexOf(c) !== -1; - } - - function isErrorCharacter(c) { - return "()".indexOf(c) !== -1; + return "=,>-])".indexOf(c) !== -1; } function itemTypeFromName(typename) { @@ -585,8 +593,6 @@ function initSearch(rawSearchIndex) { throw ["Unexpected ", "!", ": it can only be at the end of an ident"]; } foundExclamation = parserState.pos; - } else if (isErrorCharacter(c)) { - throw ["Unexpected ", c]; } else if (isPathSeparator(c)) { if (c === ":") { if (!isPathStart(parserState)) { @@ -616,11 +622,14 @@ function initSearch(rawSearchIndex) { } } else if ( c === "[" || + c === "(" || isEndCharacter(c) || isSpecialStartCharacter(c) || isSeparatorCharacter(c) ) { break; + } else if (parserState.pos > 0) { + throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]]; } else { throw ["Unexpected ", c]; } @@ -661,15 +670,24 @@ function initSearch(rawSearchIndex) { skipWhitespace(parserState); let start = parserState.pos; let end; - if (parserState.userQuery[parserState.pos] === "[") { + if ("[(".indexOf(parserState.userQuery[parserState.pos]) !== -1) { +let endChar = ")"; +let name = "()"; +let friendlyName = "tuple"; + +if (parserState.userQuery[parserState.pos] === "[") { + endChar = "]"; + name = "[]"; + friendlyName = "slice"; +} parserState.pos += 1; - getItemsBefore(query, parserState, generics, "]"); + const { foundSeparator } = getItemsBefore(query, parserState, generics, endChar); const typeFilter = parserState.typeFilter; const isInBinding = parserState.isInBinding; if (typeFilter !== null && typeFilter !== "primitive") { throw [ "Invalid search type: primitive ", - "[]", + name, " and ", typeFilter, " both specified", @@ -677,27 +695,31 @@ function initSearch(rawSearchIndex) { } parserState.typeFilter = null; parserState.isInBinding = null; - parserState.totalElems += 1; - if (isInGenerics) { - parserState.genericsElems += 1; - } for (const gen of generics) { if (gen.bindingName !== null) { - throw ["Type parameter ", "=", " cannot be within slice ", "[]"]; + throw ["Type parameter ", "=", ` cannot be within ${friendlyName} `, name]; } } - elems.push({ - name: "[]", - id: null, - fullPath: ["[]"], - pathWithoutLast: [], - pathLast: "[]", - normalizedPathLast: "[]", - generics, - typeFilter: "primitive", - bindingName: isInBinding, - bindings: new Map(), - }); + if (name === "()" && !foundSeparator && generics.length === 1 && typeFilter === null) { + elems.push(generics[0]); + } else { + parserState.totalElems += 1; + if (isInGenerics) { + parserState.genericsElems += 1; + } + elems.push({ + name: name, + id: null, + fullPath: [name], + pathWithoutLast: [], + pathLast: name, + normalizedPathLast: name, + generics, + bindings: new Map(), + typeFilter: "primitive", + bindingName: isInBinding, + }); + } } else { const isStringElem = parserState.userQuery[start] === "\""; // We handle the strings on their own mostly to make code easier to follow. @@ -770,9 +792,11 @@ function initSearch(rawSearchIndex) { * @param {Array} elems - This is where the new {QueryElement} will be added. * @param {string} endChar - This function will stop when it'll encounter this * character. + * @returns {{foundSeparator: bool}} */ function getItemsBefore(query, parserState, elems, endChar) { let foundStopChar = true; + let foundSeparator = false; let start = parserState.pos; // If this is a generic, keep the outer item's type filter around. @@ -786,6 +810,8 @@ function initSearch(rawSearchIndex) { extra = "<"; } else if (endChar === "]") { extra = "["; + } else if (endChar === ")") { + extra = "("; } else if (endChar === "") { extra = "->"; } else { @@ -802,6 +828,7 @@ function initSearch(rawSearchIndex) { } else if (isSeparatorCharacter(c)) { parserState.pos += 1; foundStopChar = true; + foundSeparator = true; continue; } else if (c === ":" && isPathStart(parserState)) { throw ["Unexpected ", "::", ": paths cannot start with ", "::"]; @@ -879,6 +906,8 @@ function initSearch(rawSearchIndex) { parserState.typeFilter = oldTypeFilter; parserState.isInBinding = oldIsInBinding; + + return { foundSeparator }; } /** @@ -926,6 +955,8 @@ function initSearch(rawSearchIndex) { break; } throw ["Unexpected ", c, " (did you mean ", "->", "?)"]; + } else if (parserState.pos > 0) { + throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]]; } throw ["Unexpected ", c]; } else if (c === ":" && !isPathStart(parserState)) { @@ -1599,6 +1630,11 @@ function initSearch(rawSearchIndex) { ) { // [] matches primitive:array or primitive:slice // if it matches, then we're fine, and this is an appropriate match candidate + } else if (queryElem.id === typeNameIdOfTupleOrUnit && + (fnType.id === typeNameIdOfTuple || fnType.id === typeNameIdOfUnit) + ) { + // () matches primitive:tuple or primitive:unit + // if it matches, then we're fine, and this is an appropriate match candidate } else if (fnType.id !== queryElem.id || queryElem.id === null) { return false; } @@ -1792,7 +1828,7 @@ function initSearch(rawSearchIndex) { if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 && // special case - elem.id !== typeNameIdOfArrayOrSlice + elem.id !== typeNameIdOfArrayOrSlice && elem.id !== typeNameIdOfTupleOrUnit ) { return row.id === elem.id || checkIfInList( row.generics, @@ -2822,12 +2858,15 @@ ${item.displayPath}${name}\ */ function buildFunctionTypeFingerprint(type, output, fps) { let input = type.id; - // All forms of `[]` get collapsed down to one thing in the bloom filter. + // All forms of `[]`/`()` get collapsed down to one thing in the bloom filter. // Differentiating between arrays and slices, if the user asks for it, is // still done in the matching algorithm. if (input === typeNameIdOfArray || input === typeNameIdOfSlice) { input = typeNameIdOfArrayOrSlice; } + if (input === typeNameIdOfTuple || input === typeNameIdOfUnit) { + input = typeNameIdOfTupleOrUnit; + } // http://burtleburtle.net/bob/hash/integer.html // ~~ is toInt32. It's used before adding, so // the number stays in safe integer range. @@ -2922,7 +2961,10 @@ ${item.displayPath}${name}\ // that can be searched using `[]` syntax. typeNameIdOfArray = buildTypeMapIndex("array"); typeNameIdOfSlice = buildTypeMapIndex("slice"); + typeNameIdOfTuple = buildTypeMapIndex("tuple"); + typeNameIdOfUnit = buildTypeMapIndex("unit"); typeNameIdOfArrayOrSlice = buildTypeMapIndex("[]"); + typeNameIdOfTupleOrUnit = buildTypeMapIndex("()"); // Function type fingerprints are 128-bit bloom filters that are used to // estimate the distance between function and query. diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index f9f9c4f4de8c..16d171260dad 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -24,7 +24,7 @@ const PARSED = [ original: "-> *", returned: [], userQuery: "-> *", - error: "Unexpected `*`", + error: "Unexpected `*` after ` `", }, { query: 'a<"P">', @@ -107,15 +107,6 @@ const PARSED = [ userQuery: "a<::a>", error: "Unexpected `::`: paths cannot start with `::`", }, - { - query: "((a))", - elems: [], - foundElems: 0, - original: "((a))", - returned: [], - userQuery: "((a))", - error: "Unexpected `(`", - }, { query: "(p -> p", elems: [], @@ -123,7 +114,7 @@ const PARSED = [ original: "(p -> p", returned: [], userQuery: "(p -> p", - error: "Unexpected `(`", + error: "Unexpected `-` after `(`", }, { query: "::a::b", @@ -204,7 +195,7 @@ const PARSED = [ original: "a (b:", returned: [], userQuery: "a (b:", - error: "Unexpected `(`", + error: "Expected `,`, `:` or `->`, found `(`", }, { query: "_:", @@ -249,7 +240,7 @@ const PARSED = [ original: "ab'", returned: [], userQuery: "ab'", - error: "Unexpected `'`", + error: "Unexpected `'` after `b`", }, { query: "a->", diff --git a/tests/rustdoc-js-std/parser-slice-array.js b/tests/rustdoc-js-std/parser-slice-array.js index 239391bed420..1de52af94e6b 100644 --- a/tests/rustdoc-js-std/parser-slice-array.js +++ b/tests/rustdoc-js-std/parser-slice-array.js @@ -266,6 +266,24 @@ const PARSED = [ userQuery: "]", error: "Unexpected `]`", }, + { + query: '[a', + elems: [], + foundElems: 0, + original: "[a", + returned: [], + userQuery: "[a", + error: "Unclosed `[`", + }, + { + query: 'a]', + elems: [], + foundElems: 0, + original: "a]", + returned: [], + userQuery: "a]", + error: "Unexpected `]` after `>`", + }, { query: 'primitive:[u8]', elems: [ diff --git a/tests/rustdoc-js-std/parser-tuple.js b/tests/rustdoc-js-std/parser-tuple.js new file mode 100644 index 000000000000..eb16289d3c05 --- /dev/null +++ b/tests/rustdoc-js-std/parser-tuple.js @@ -0,0 +1,365 @@ +const PARSED = [ + { + query: '(((D, ()))', + elems: [], + foundElems: 0, + original: '(((D, ()))', + returned: [], + userQuery: '(((d, ()))', + error: 'Unclosed `(`', + }, + { + query: '(((D, ())))', + elems: [ + { + name: "()", + fullPath: ["()"], + pathWithoutLast: [], + pathLast: "()", + generics: [ + { + name: "d", + fullPath: ["d"], + pathWithoutLast: [], + pathLast: "d", + generics: [], + typeFilter: -1, + }, + { + name: "()", + fullPath: ["()"], + pathWithoutLast: [], + pathLast: "()", + generics: [], + typeFilter: 1, + }, + ], + typeFilter: 1, + } + ], + foundElems: 1, + original: '(((D, ())))', + returned: [], + userQuery: '(((d, ())))', + error: null, + }, + { + query: '(),u8', + elems: [ + { + name: "()", + fullPath: ["()"], + pathWithoutLast: [], + pathLast: "()", + generics: [], + typeFilter: 1, + }, + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [], + typeFilter: -1, + }, + ], + foundElems: 2, + original: "(),u8", + returned: [], + userQuery: "(),u8", + error: null, + }, + // Parens act as grouping operators when: + // - there's no commas directly nested within + // - there's at least two child types (zero means unit) + // - it's not tagged with a type filter + // Otherwise, they represent unit and/or tuple. To search for + // unit or tuple specifically, use `primitive:unit` or `primitive:tuple<...>`. + { + query: '(u8)', + elems: [ + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [], + typeFilter: -1, + }, + ], + foundElems: 1, + original: "(u8)", + returned: [], + userQuery: "(u8)", + error: null, + }, + { + query: '(u8,)', + elems: [ + { + name: "()", + fullPath: ["()"], + pathWithoutLast: [], + pathLast: "()", + generics: [ + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [], + typeFilter: -1, + }, + ], + typeFilter: 1, + }, + ], + foundElems: 1, + original: "(u8,)", + returned: [], + userQuery: "(u8,)", + error: null, + }, + { + query: '(,u8)', + elems: [ + { + name: "()", + fullPath: ["()"], + pathWithoutLast: [], + pathLast: "()", + generics: [ + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [], + typeFilter: -1, + }, + ], + typeFilter: 1, + }, + ], + foundElems: 1, + original: "(,u8)", + returned: [], + userQuery: "(,u8)", + error: null, + }, + { + query: 'primitive:(u8)', + elems: [ + { + name: "()", + fullPath: ["()"], + pathWithoutLast: [], + pathLast: "()", + generics: [ + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [], + typeFilter: -1, + }, + ], + typeFilter: 1, + }, + ], + foundElems: 1, + original: "primitive:(u8)", + returned: [], + userQuery: "primitive:(u8)", + error: null, + }, + { + query: '(primitive:u8)', + elems: [ + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [], + typeFilter: 1, + }, + ], + foundElems: 1, + original: "(primitive:u8)", + returned: [], + userQuery: "(primitive:u8)", + error: null, + }, + { + query: '(u8,u8)', + elems: [ + { + name: "()", + fullPath: ["()"], + pathWithoutLast: [], + pathLast: "()", + generics: [ + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [], + typeFilter: -1, + }, + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [], + typeFilter: -1, + }, + ], + typeFilter: 1, + }, + ], + foundElems: 1, + original: "(u8,u8)", + returned: [], + userQuery: "(u8,u8)", + error: null, + }, + { + query: '(u8)', + elems: [ + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [ + { + name: "u8", + fullPath: ["u8"], + pathWithoutLast: [], + pathLast: "u8", + generics: [], + typeFilter: -1, + }, + ], + typeFilter: -1, + }, + ], + foundElems: 1, + original: "(u8)", + returned: [], + userQuery: "(u8)", + error: null, + }, + { + query: '()', + elems: [ + { + name: "()", + fullPath: ["()"], + pathWithoutLast: [], + pathLast: "()", + generics: [], + typeFilter: 1, + }, + ], + foundElems: 1, + original: "()", + returned: [], + userQuery: "()", + error: null, + }, + { + query: '(>', + elems: [], + foundElems: 0, + original: "(>", + returned: [], + userQuery: "(>", + error: "Unexpected `>` after `(`", + }, + { + query: '(<', + elems: [], + foundElems: 0, + original: "(<", + returned: [], + userQuery: "(<", + error: "Found generics without a path", + }, + { + query: '(a>', + elems: [], + foundElems: 0, + original: "(a>", + returned: [], + userQuery: "(a>", + error: "Unexpected `>` after `(`", + }, + { + query: '(a<', + elems: [], + foundElems: 0, + original: "(a<", + returned: [], + userQuery: "(a<", + error: "Unclosed `<`", + }, + { + query: '(a', + elems: [], + foundElems: 0, + original: "(a", + returned: [], + userQuery: "(a", + error: "Unclosed `(`", + }, + { + query: '(', + elems: [], + foundElems: 0, + original: "(", + returned: [], + userQuery: "(", + error: "Unclosed `(`", + }, + { + query: ')', + elems: [], + foundElems: 0, + original: ")", + returned: [], + userQuery: ")", + error: "Unexpected `)`", + }, + { + query: '(a', + elems: [], + foundElems: 0, + original: "(a", + returned: [], + userQuery: "(a", + error: "Unclosed `(`", + }, + { + query: 'a)', + elems: [], + foundElems: 0, + original: "a)", + returned: [], + userQuery: "a)", + error: "Unexpected `)` after `>`", + }, + { + query: 'macro:(u8)', + elems: [], + foundElems: 0, + original: "macro:(u8)", + returned: [], + userQuery: "macro:(u8)", + error: "Invalid search type: primitive `()` and `macro` both specified", + }, +]; diff --git a/tests/rustdoc-js-std/parser-weird-queries.js b/tests/rustdoc-js-std/parser-weird-queries.js index ba68c9717c51..26b8c32d6805 100644 --- a/tests/rustdoc-js-std/parser-weird-queries.js +++ b/tests/rustdoc-js-std/parser-weird-queries.js @@ -44,7 +44,7 @@ const PARSED = [ original: "a,b(c)", returned: [], userQuery: "a,b(c)", - error: "Unexpected `(`", + error: "Expected `,`, `:` or `->`, found `(`", }, { query: 'aaa,a', diff --git a/tests/rustdoc-js/tuple-unit.js b/tests/rustdoc-js/tuple-unit.js new file mode 100644 index 000000000000..d24a3da328c5 --- /dev/null +++ b/tests/rustdoc-js/tuple-unit.js @@ -0,0 +1,80 @@ +// exact-check + +const EXPECTED = [ + { + 'query': '()', + 'returned': [ + { 'path': 'tuple_unit', 'name': 'side_effect' }, + { 'path': 'tuple_unit', 'name': 'one' }, + { 'path': 'tuple_unit', 'name': 'two' }, + { 'path': 'tuple_unit', 'name': 'nest' }, + ], + 'in_args': [], + }, + { + 'query': 'primitive:unit', + 'returned': [ + { 'path': 'tuple_unit', 'name': 'side_effect' }, + ], + 'in_args': [], + }, + { + 'query': 'primitive:tuple', + 'returned': [ + { 'path': 'tuple_unit', 'name': 'one' }, + { 'path': 'tuple_unit', 'name': 'two' }, + { 'path': 'tuple_unit', 'name': 'nest' }, + ], + 'in_args': [], + }, + { + 'query': '(P)', + 'returned': [ + { 'path': 'tuple_unit', 'name': 'not_tuple' }, + { 'path': 'tuple_unit', 'name': 'one' }, + { 'path': 'tuple_unit', 'name': 'two' }, + ], + 'in_args': [], + }, + { + 'query': '(P,)', + 'returned': [ + { 'path': 'tuple_unit', 'name': 'one' }, + { 'path': 'tuple_unit', 'name': 'two' }, + ], + 'in_args': [], + }, + { + 'query': '(P, P)', + 'returned': [ + { 'path': 'tuple_unit', 'name': 'two' }, + ], + 'in_args': [], + }, + { + 'query': '(P, ())', + 'returned': [], + 'in_args': [], + }, + { + 'query': '(Q, ())', + 'returned': [ + { 'path': 'tuple_unit', 'name': 'nest' }, + ], + 'in_args': [], + }, + { + 'query': '(R)', + 'returned': [ + { 'path': 'tuple_unit', 'name': 'nest' }, + ], + 'in_args': [], + }, + { + 'query': '(u32)', + 'returned': [ + { 'path': 'tuple_unit', 'name': 'nest' }, + ], + 'in_args': [], + }, +]; diff --git a/tests/rustdoc-js/tuple-unit.rs b/tests/rustdoc-js/tuple-unit.rs new file mode 100644 index 000000000000..93f9a671cbc3 --- /dev/null +++ b/tests/rustdoc-js/tuple-unit.rs @@ -0,0 +1,18 @@ +pub struct P; +pub struct Q; +pub struct R(T); + +// Checks that tuple and unit both work +pub fn side_effect() { } + +// Check a non-tuple +pub fn not_tuple() -> P { loop {} } + +// Check a 1-tuple +pub fn one() -> (P,) { loop {} } + +// Check a 2-tuple +pub fn two() -> (P,P) { loop {} } + +// Check a nested tuple +pub fn nest() -> (Q, R<(u32,)>) { loop {} } From e74339bd4b70ee93ca380c496e53177fd19193af Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 22 Nov 2023 18:20:31 -0700 Subject: [PATCH 011/257] Add a few more docs --- .../rustdoc/src/read-documentation/search.md | 48 ++++++------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index d773794504e7..b5f4060f0590 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -147,45 +147,20 @@ will match these queries: * `Read -> Result, Error>` * `Read -> Result` * `Read -> Result>` +* `Read -> u8` But it *does not* match `Result` or `Result>`. ### Primitives with Special Syntax - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ShorthandExplicit names
[]primitive:slice and/or primitive:array
[T]primitive:slice<T> and/or primitive:array<T>
()primitive:unit and/or primitive:tuple
(T)T
(T,)primitive:tuple<T>
!primitive:never
+| Shorthand | Explicit names | +| --------- | ------------------------------------------------ | +| `[]` | `primitive:slice` and/or `primitive:array` | +| `[T]` | `primitive:slice` and/or `primitive:array` | +| `()` | `primitive:unit` and/or `primitive:tuple` | +| `(T)` | `T` | +| `(T,)` | `primitive:tuple` | +| `!` | `primitive:never` | When searching for `[]`, Rustdoc will return search results with either slices or arrays. If you know which one you want, you can force it to return results @@ -200,6 +175,11 @@ since parens act as the grouping operator. If they're empty, though, they will match both `unit` and `tuple`, and if there's more than one type (or a trailing or leading comma) it is the same as `primitive:tuple<...>`. +However, since items can be left out of the query, `(T)` will still return +results for types that match tuples, even though it also matches the type on +its own. That is, `(u32)` matches `(u32,)` for the exact same reason that it +also matches `Result`. + ### Limitations and quirks of type-based search Type-based search is still a buggy, experimental, work-in-progress feature. From a38a79e2ba12c80459b81d21be8a59aeb9434f5d Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Wed, 27 Dec 2023 19:49:50 +0800 Subject: [PATCH 012/257] use SyntaxKind instead of "Self" literal comparison --- .../extract_struct_from_enum_variant.rs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 2e0a628eee7c..3aa3069a23de 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -250,24 +250,21 @@ fn create_struct_def( field_list .fields() .filter_map(|field| match field.ty()? { - ast::Type::PathType(p) => { - let generic_arg_list = p.path()?.segment()?.generic_arg_list()?; - Some( - generic_arg_list - .generic_args() - .filter_map(|generic_arg| { - if generic_arg.to_string() == "Self" { - let type_arg = - make::type_arg(make::ty(&enum_.name()?.to_string())) - .clone_for_update(); - Some(ted::replace(generic_arg.syntax(), type_arg.syntax())) - } else { - None - } - }) - .count(), - ) - } + ast::Type::PathType(p) => Some( + p.syntax() + .descendants_with_tokens() + .filter_map(|it| { + if it.kind() == T![Self] { + let type_arg = + make::type_arg(make::ty(&enum_.name()?.to_string())) + .clone_for_update(); + Some(ted::replace(it, type_arg.syntax())) + } else { + None + } + }) + .count(), + ), _ => None, }) .count(); From 6a7d3f1c3eda7b46bb4ef7f35a9560ae0d87e97e Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Wed, 27 Dec 2023 19:55:36 +0800 Subject: [PATCH 013/257] add test case for nested generic arg with `Self` --- .../extract_struct_from_enum_variant.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 3aa3069a23de..9f81fd932f66 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -461,6 +461,23 @@ enum Foo { r#" struct Bar{ node: Box } +enum Foo { + Bar(Bar), + Nil, +} +"#, + ); + check_assist( + extract_struct_from_enum_variant, + r#" +enum Foo { + Bar $0{ node: Box, a: Arc> }, + Nil, +} +"#, + r#" +struct Bar{ node: Box, a: Arc> } + enum Foo { Bar(Bar), Nil, From 52f7575c35eb081c615a902dead2308b0f1a8570 Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Fri, 29 Dec 2023 20:40:37 +0300 Subject: [PATCH 014/257] minor: chore: fix typos and urls in docs/dev/guide.md --- docs/dev/guide.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/dev/guide.md b/docs/dev/guide.md index a5f1811bf24a..059ff7389c7e 100644 --- a/docs/dev/guide.md +++ b/docs/dev/guide.md @@ -65,11 +65,11 @@ Next, let's talk about what the inputs to the `Analysis` are, precisely. rust-analyzer never does any I/O itself, all inputs get passed explicitly via the `AnalysisHost::apply_change` method, which accepts a single argument, a -`Change`. [`Change`] is a builder for a single change +`AnalysisChange`. [`AnalysisChange`] is a builder for a single change "transaction", so it suffices to study its methods to understand all of the input data. -[`Change`]: https://github.com/rust-lang/rust-analyzer/blob/master/crates/base_db/src/change.rs#L14-L89 +[`AnalysisChange`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L119-L167 The `(add|change|remove)_file` methods control the set of the input files, where each file has an integer id (`FileId`, picked by the client), text (`String`) @@ -118,7 +118,7 @@ can have `#[path="/dev/random"] mod foo;`. To solve (or explicitly refuse to solve) these problems rust-analyzer uses the concept of a "source root". Roughly speaking, source roots are the contents of a -directory on a file systems, like `/home/matklad/projects/rustraytracer/**.rs`. +directory on a file system, like `/home/matklad/projects/rustraytracer/**.rs`. More precisely, all files (`FileId`s) are partitioned into disjoint `SourceRoot`s. Each file has a relative UTF-8 path within the `SourceRoot`. @@ -156,7 +156,7 @@ it should be possible to dynamically reconfigure it later without restart. [main_loop.rs#L62-L70](https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L62-L70) The [`ProjectModel`] we get after this step is very Cargo and sysroot specific, -it needs to be lowered to get the input in the form of `Change`. This +it needs to be lowered to get the input in the form of `AnalysisChange`. This happens in [`ServerWorldState::new`] method. Specifically * Create a `SourceRoot` for each Cargo package and sysroot. @@ -173,7 +173,7 @@ of the main loop, just like any other change. Here's where we handle: * [File system changes](https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L194) * [Changes from the editor](https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L377) -After a single loop's turn, we group the changes into one `Change` and +After a single loop's turn, we group the changes into one `AnalysisChange` and [apply] it. This always happens on the main thread and blocks the loop. [apply]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/server_world.rs#L216 @@ -186,7 +186,7 @@ executing "goto definition" on the threadpool and a new change comes in, the task will be canceled as soon as the main loop calls `apply_change` on the `AnalysisHost`. -["goto definition"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/server_world.rs#L216 +["goto definition"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L296 [`schedule`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L426-L455 [The task]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop/handlers.rs#L205-L223 @@ -250,13 +250,13 @@ All analyzer information is stored in a salsa database. `Analysis` and `AnalysisHost` types are newtype wrappers for [`RootDatabase`] -- a salsa database. -[`RootDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/db.rs#L88-L134 +[`RootDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/db.rs#L88-L134 Salsa input queries are defined in [`FilesDatabase`] (which is a part of -`RootDatabase`). They closely mirror the familiar `Change` structure: +`RootDatabase`). They closely mirror the familiar `AnalysisChange` structure: indeed, what `apply_change` does is it sets the values of input queries. -[`FilesDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/base_db/src/input.rs#L150-L174 +[`FilesDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_db/src/input.rs#L150-L174 ## From text to semantic model @@ -281,7 +281,7 @@ methods invoke various queries on the database to build the model on demand. Here's [the list of queries]. [`code_model_api`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/code_model_api.rs -[the list of queries]: https://github.com/rust-lang/rust-analyzer/blob/7e84440e25e19529e4ff8a66e521d1b06349c6ec/crates/ra_hir/src/db.rs#L20-L106 +[the list of queries]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/db.rs#L20-L106 The first step of building the model is parsing the source code. @@ -493,7 +493,7 @@ position-independent part of the lowering. The result of this query is stable. Naturally, name resolution [uses] this stable projection query. [imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59 -[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59 +[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L74-L94 [projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L97-L103 [uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/query_definitions.rs#L49 @@ -560,8 +560,7 @@ the type to completion. [receiving a message]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L203 [schedule it on the threadpool]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L428 [catch]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L436-L442 -[the handler]: https://salsa.zulipchat.com/#narrow/stream/181542-rfcs.2Fsalsa-query-group/topic/design.20next.20steps -[ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/lib.rs#L439-L444 +[the handler]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop/handlers.rs#L304-L343 [ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L439-L444 [completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L46-L62 [`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L14-L37 From 786e0bb1dcfdb9deee31ae7b181692467191ea2a Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sat, 30 Dec 2023 02:54:40 +0800 Subject: [PATCH 015/257] bootstrap: Move -Clto= setting from Rustc::run to rustc_cargo It prevents a full rebuild of stage 1 compiler when issuing "x.py test" with rust.lto != thin-local in config.toml. --- src/bootstrap/src/core/build_steps/compile.rs | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index dbb64583d561..8e1f3b03d786 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -905,34 +905,6 @@ impl Step for Rustc { )); } - // We currently don't support cross-crate LTO in stage0. This also isn't hugely necessary - // and may just be a time sink. - if compiler.stage != 0 { - match builder.config.rust_lto { - RustcLto::Thin | RustcLto::Fat => { - // Since using LTO for optimizing dylibs is currently experimental, - // we need to pass -Zdylib-lto. - cargo.rustflag("-Zdylib-lto"); - // Cargo by default passes `-Cembed-bitcode=no` and doesn't pass `-Clto` when - // compiling dylibs (and their dependencies), even when LTO is enabled for the - // crate. Therefore, we need to override `-Clto` and `-Cembed-bitcode` here. - let lto_type = match builder.config.rust_lto { - RustcLto::Thin => "thin", - RustcLto::Fat => "fat", - _ => unreachable!(), - }; - cargo.rustflag(&format!("-Clto={lto_type}")); - cargo.rustflag("-Cembed-bitcode=yes"); - } - RustcLto::ThinLocal => { /* Do nothing, this is the default */ } - RustcLto::Off => { - cargo.rustflag("-Clto=off"); - } - } - } else if builder.config.rust_lto == RustcLto::Off { - cargo.rustflag("-Clto=off"); - } - for krate in &*self.crates { cargo.arg("-p").arg(krate); } @@ -989,6 +961,34 @@ pub fn rustc_cargo(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelec cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)"); + // We currently don't support cross-crate LTO in stage0. This also isn't hugely necessary + // and may just be a time sink. + if stage != 0 { + match builder.config.rust_lto { + RustcLto::Thin | RustcLto::Fat => { + // Since using LTO for optimizing dylibs is currently experimental, + // we need to pass -Zdylib-lto. + cargo.rustflag("-Zdylib-lto"); + // Cargo by default passes `-Cembed-bitcode=no` and doesn't pass `-Clto` when + // compiling dylibs (and their dependencies), even when LTO is enabled for the + // crate. Therefore, we need to override `-Clto` and `-Cembed-bitcode` here. + let lto_type = match builder.config.rust_lto { + RustcLto::Thin => "thin", + RustcLto::Fat => "fat", + _ => unreachable!(), + }; + cargo.rustflag(&format!("-Clto={lto_type}")); + cargo.rustflag("-Cembed-bitcode=yes"); + } + RustcLto::ThinLocal => { /* Do nothing, this is the default */ } + RustcLto::Off => { + cargo.rustflag("-Clto=off"); + } + } + } else if builder.config.rust_lto == RustcLto::Off { + cargo.rustflag("-Clto=off"); + } + rustc_cargo_env(builder, cargo, target, stage); } From 1b7968a2cbdc54c517f108b424ddb64ae817f08a Mon Sep 17 00:00:00 2001 From: austaras Date: Mon, 11 Dec 2023 16:26:27 +0800 Subject: [PATCH 016/257] fix: try obligation of `IndexMut` when infer --- crates/hir-def/src/body/lower.rs | 3 +- crates/hir-def/src/body/pretty.rs | 2 +- crates/hir-def/src/hir.rs | 3 +- crates/hir-ty/src/infer/closure.rs | 2 +- crates/hir-ty/src/infer/expr.rs | 25 +++++++++---- crates/hir-ty/src/infer/mutability.rs | 2 +- crates/hir-ty/src/mir/lower/as_place.rs | 2 +- crates/hir-ty/src/tests/traits.rs | 47 +++++++++++++++++++++++++ 8 files changed, 74 insertions(+), 12 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index a45ec844aba0..bc4da360c5a2 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -622,7 +622,8 @@ impl ExprCollector<'_> { ast::Expr::IndexExpr(e) => { let base = self.collect_expr_opt(e.base()); let index = self.collect_expr_opt(e.index()); - self.alloc_expr(Expr::Index { base, index }, syntax_ptr) + let is_assignee_expr = self.is_lowering_assignee_expr; + self.alloc_expr(Expr::Index { base, index, is_assignee_expr }, syntax_ptr) } ast::Expr::RangeExpr(e) => { let lhs = e.start().map(|lhs| self.collect_expr(lhs)); diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 6ecf1c20d6c1..02b19ade44ba 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -376,7 +376,7 @@ impl Printer<'_> { w!(self, ") "); } } - Expr::Index { base, index } => { + Expr::Index { base, index, is_assignee_expr: _ } => { self.print_expr(*base); w!(self, "["); self.print_expr(*index); diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 591ee77c70a4..5890e818c46f 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -265,6 +265,7 @@ pub enum Expr { Index { base: ExprId, index: ExprId, + is_assignee_expr: bool, }, Closure { args: Box<[PatId]>, @@ -432,7 +433,7 @@ impl Expr { f(rhs); } } - Expr::Index { base, index } => { + Expr::Index { base, index, .. } => { f(*base); f(*index); } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index af74df1032cb..58b4f29ec8c5 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -598,7 +598,7 @@ impl InferenceContext<'_> { self.consume_expr(expr); } } - Expr::Index { base, index } => { + Expr::Index { base, index, is_assignee_expr: _ } => { self.select_from_expr(*base); self.consume_expr(*index); } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index a5e77a12d8c5..2fd53c97101e 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -744,7 +744,7 @@ impl InferenceContext<'_> { (RangeOp::Inclusive, _, None) => self.err_ty(), } } - Expr::Index { base, index } => { + Expr::Index { base, index, is_assignee_expr } => { let base_ty = self.infer_expr_inner(*base, &Expectation::none()); let index_ty = self.infer_expr(*index, &Expectation::none()); @@ -772,11 +772,24 @@ impl InferenceContext<'_> { .build(); self.write_method_resolution(tgt_expr, func, substs); } - self.resolve_associated_type_with_params( - self_ty, - self.resolve_ops_index_output(), - &[index_ty.cast(Interner)], - ) + let assoc = self.resolve_ops_index_output(); + let res = self.resolve_associated_type_with_params( + self_ty.clone(), + assoc, + &[index_ty.clone().cast(Interner)], + ); + + if *is_assignee_expr { + if let Some(index_trait) = self.resolve_lang_trait(LangItem::IndexMut) { + let trait_ref = TyBuilder::trait_ref(self.db, index_trait) + .push(self_ty) + .fill(|_| index_ty.clone().cast(Interner)) + .build(); + self.push_obligation(trait_ref.cast(Interner)); + } + } + + res } else { self.err_ty() } diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index b8a1af96fba6..663ea8532318 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -96,7 +96,7 @@ impl InferenceContext<'_> { Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => { self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) } - &Expr::Index { base, index } => { + &Expr::Index { base, index, is_assignee_expr: _ } => { if mutability == Mutability::Mut { if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) { if let Some(index_trait) = self diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index 8c078eb4ad75..cb5588a5c13d 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -218,7 +218,7 @@ impl MirLowerCtx<'_> { self.push_field_projection(&mut r, expr_id)?; Ok(Some((r, current))) } - Expr::Index { base, index } => { + Expr::Index { base, index, is_assignee_expr: _ } => { let base_ty = self.expr_ty_after_adjustments(*base); let index_ty = self.expr_ty_after_adjustments(*index); if index_ty != TyBuilder::usize() diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 003ae60e8e51..d270328605a9 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4506,3 +4506,50 @@ fn ttt() { "#, ); } + +#[test] +fn infer_borrow() { + check_types( + r#" +//- minicore: index +pub struct SomeMap; + +pub trait Borrow { + fn borrow(&self) -> &Borrowed; +} + +impl Borrow for T { + fn borrow(&self) -> &T { + self + } +} + +impl Borrow for &T { + fn borrow(&self) -> &T { + &**self + } +} + +impl> core::ops::Index for SomeMap { + type Output = (); + + fn index(&self, _: KB) -> &() { + &() + } +} + +impl core::ops::IndexMut for SomeMap { + fn index_mut(&mut self, _: K) -> &mut () { + &mut () + } +} + +fn foo() { + let mut map = SomeMap; + map["a"] = (); + map; + //^^^ SomeMap<&str> +} +"#, + ); +} From f83468c89b706e686ff2d991299789050ea493de Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 3 May 2023 18:39:21 +0000 Subject: [PATCH 017/257] Inline dominator check. --- compiler/rustc_data_structures/src/graph/dominators/mod.rs | 1 + compiler/rustc_middle/src/mir/mod.rs | 2 ++ compiler/rustc_mir_transform/src/ssa.rs | 1 + 3 files changed, 4 insertions(+) diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 4b819e1cbd61..a45f1dd72a12 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -394,6 +394,7 @@ impl Dominators { /// # Panics /// /// Panics if `b` is unreachable. + #[inline] pub fn dominates(&self, a: Node, b: Node) -> bool { match &self.kind { Kind::Path => a.index() <= b.index(), diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 5c425fef27eb..0a35afddf400 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1604,6 +1604,7 @@ impl Location { false } + #[inline] pub fn dominates(&self, other: Location, dominators: &Dominators) -> bool { if self.block == other.block { self.statement_index <= other.statement_index @@ -1623,6 +1624,7 @@ pub enum DefLocation { } impl DefLocation { + #[inline] pub fn dominates(self, location: Location, dominators: &Dominators) -> bool { match self { DefLocation::Argument => true, diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 3a6e1ef34883..1ed3b14e7550 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -94,6 +94,7 @@ impl SsaLocals { self.direct_uses[local] } + #[inline] pub fn assignment_dominates( &self, dominators: &Dominators, From b509e2dd00a6673704b32f2a1a96d194782b62b4 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 3 May 2023 18:40:09 +0000 Subject: [PATCH 018/257] Inline successor_within_block. --- compiler/rustc_middle/src/mir/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 0a35afddf400..105ac66e4a83 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1568,6 +1568,7 @@ impl Location { /// /// Note that if this location represents a terminator, then the /// resulting location would be out of bounds and invalid. + #[inline] pub fn successor_within_block(&self) -> Location { Location { block: self.block, statement_index: self.statement_index + 1 } } From 97758611e72d48f9e9e99f241403194e3b0c8b05 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 16 May 2023 08:18:20 +0000 Subject: [PATCH 019/257] Inline utils. --- compiler/rustc_middle/src/mir/terminator.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 7be6deb61419..385237b357b4 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -26,6 +26,7 @@ impl SwitchTargets { } /// Inverse of `SwitchTargets::static_if`. + #[inline] pub fn as_static_if(&self) -> Option<(u128, BasicBlock, BasicBlock)> { if let &[value] = &self.values[..] && let &[then, else_] = &self.targets[..] @@ -37,6 +38,7 @@ impl SwitchTargets { } /// Returns the fallback target that is jumped to when none of the values match the operand. + #[inline] pub fn otherwise(&self) -> BasicBlock { *self.targets.last().unwrap() } @@ -47,15 +49,18 @@ impl SwitchTargets { /// including the `otherwise` fallback target. /// /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory. + #[inline] pub fn iter(&self) -> SwitchTargetsIter<'_> { SwitchTargetsIter { inner: iter::zip(&self.values, &self.targets) } } /// Returns a slice with all possible jump targets (including the fallback target). + #[inline] pub fn all_targets(&self) -> &[BasicBlock] { &self.targets } + #[inline] pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] { &mut self.targets } @@ -63,6 +68,7 @@ impl SwitchTargets { /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the /// specific value. This cannot fail, as it'll return the `otherwise` /// branch if there's not a specific match for the value. + #[inline] pub fn target_for_value(&self, value: u128) -> BasicBlock { self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise()) } @@ -75,10 +81,12 @@ pub struct SwitchTargetsIter<'a> { impl<'a> Iterator for SwitchTargetsIter<'a> { type Item = (u128, BasicBlock); + #[inline] fn next(&mut self) -> Option { self.inner.next().map(|(val, bb)| (*val, *bb)) } + #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } @@ -330,28 +338,34 @@ pub type SuccessorsMut<'a> = iter::Chain, slice::IterMut<'a, BasicBlock>>; impl<'tcx> Terminator<'tcx> { + #[inline] pub fn successors(&self) -> Successors<'_> { self.kind.successors() } + #[inline] pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { self.kind.successors_mut() } + #[inline] pub fn unwind(&self) -> Option<&UnwindAction> { self.kind.unwind() } + #[inline] pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> { self.kind.unwind_mut() } } impl<'tcx> TerminatorKind<'tcx> { + #[inline] pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> { TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) } } + #[inline] pub fn successors(&self) -> Successors<'_> { use self::TerminatorKind::*; match *self { @@ -392,6 +406,7 @@ impl<'tcx> TerminatorKind<'tcx> { } } + #[inline] pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { use self::TerminatorKind::*; match *self { @@ -430,6 +445,7 @@ impl<'tcx> TerminatorKind<'tcx> { } } + #[inline] pub fn unwind(&self) -> Option<&UnwindAction> { match *self { TerminatorKind::Goto { .. } @@ -449,6 +465,7 @@ impl<'tcx> TerminatorKind<'tcx> { } } + #[inline] pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> { match *self { TerminatorKind::Goto { .. } @@ -468,6 +485,7 @@ impl<'tcx> TerminatorKind<'tcx> { } } + #[inline] pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> { match self { TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)), @@ -475,6 +493,7 @@ impl<'tcx> TerminatorKind<'tcx> { } } + #[inline] pub fn as_goto(&self) -> Option { match self { TerminatorKind::Goto { target } => Some(*target), From 1ab60f2a646e119db42a192771a4cdbc294fd5ff Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 30 Dec 2023 17:27:20 -0700 Subject: [PATCH 020/257] rustdoc-search: fix inaccurate type descriptions --- src/librustdoc/html/static/js/externs.js | 56 ++++++++++++++++++++++ src/librustdoc/html/static/js/search.js | 61 +++--------------------- 2 files changed, 63 insertions(+), 54 deletions(-) diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index 93709e4e830a..d24148b9556b 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -200,3 +200,59 @@ let FunctionSearchType; * }} */ let FunctionType; + +/** + * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` + * are arrays with the same length. `q`, `a`, and `c` use a sparse + * representation for compactness. + * + * `n[i]` contains the name of an item. + * + * `t[i]` contains the type of that item + * (as a string of characters that represent an offset in `itemTypes`). + * + * `d[i]` contains the description of that item. + * + * `q` contains the full paths of the items. For compactness, it is a set of + * (index, path) pairs used to create a map. If a given index `i` is + * not present, this indicates "same as the last index present". + * + * `i[i]` contains an item's parent, usually a module. For compactness, + * it is a set of indexes into the `p` array. + * + * `f` contains function signatures, or `0` if the item isn't a function. + * More information on how they're encoded can be found in rustc-dev-guide + * + * Functions are themselves encoded as arrays. The first item is a list of + * types representing the function's inputs, and the second list item is a list + * of types representing the function's output. Tuples are flattened. + * Types are also represented as arrays; the first item is an index into the `p` + * array, while the second is a list of types representing any generic parameters. + * + * b[i] contains an item's impl disambiguator. This is only present if an item + * is defined in an impl block and, the impl block's type has more than one associated + * item with the same name. + * + * `a` defines aliases with an Array of pairs: [name, offset], where `offset` + * points into the n/t/d/q/i/f arrays. + * + * `doc` contains the description of the crate. + * + * `p` is a list of path/type pairs. It is used for parents and function parameters. + * + * `c` is an array of item indices that are deprecated. + * @typedef {{ + * doc: string, + * a: Object, + * n: Array, + * t: String, + * d: Array, + * q: Array<[Number, string]>, + * i: Array, + * f: string, + * p: Array, + * b: Array<[Number, String]>, + * c: Array + * }} + */ +let RawSearchIndexCrate; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index e6263db32835..2df4f7020dfb 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2924,6 +2924,11 @@ ${item.displayPath}${name}\ return functionTypeFingerprint[(fullId * 4) + 3]; } + /** + * Convert raw search index into in-memory search index. + * + * @param {[string, RawSearchIndexCrate][]} rawSearchIndex + */ function buildIndex(rawSearchIndex) { searchIndex = []; typeNameIdMap = new Map(); @@ -2950,59 +2955,7 @@ ${item.displayPath}${name}\ // This loop actually generates the search item indexes, including // normalized names, type signature objects and fingerprints, and aliases. id = 0; - /** - * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` - * are arrays with the same length. `q`, `a`, and `c` use a sparse - * representation for compactness. - * - * `n[i]` contains the name of an item. - * - * `t[i]` contains the type of that item - * (as a string of characters that represent an offset in `itemTypes`). - * - * `d[i]` contains the description of that item. - * - * `q` contains the full paths of the items. For compactness, it is a set of - * (index, path) pairs used to create a map. If a given index `i` is - * not present, this indicates "same as the last index present". - * - * `i[i]` contains an item's parent, usually a module. For compactness, - * it is a set of indexes into the `p` array. - * - * `f[i]` contains function signatures, or `0` if the item isn't a function. - * Functions are themselves encoded as arrays. The first item is a list of - * types representing the function's inputs, and the second list item is a list - * of types representing the function's output. Tuples are flattened. - * Types are also represented as arrays; the first item is an index into the `p` - * array, while the second is a list of types representing any generic parameters. - * - * b[i] contains an item's impl disambiguator. This is only present if an item - * is defined in an impl block and, the impl block's type has more than one associated - * item with the same name. - * - * `a` defines aliases with an Array of pairs: [name, offset], where `offset` - * points into the n/t/d/q/i/f arrays. - * - * `doc` contains the description of the crate. - * - * `p` is a list of path/type pairs. It is used for parents and function parameters. - * - * `c` is an array of item indices that are deprecated. - * - * @type {{ - * doc: string, - * a: Object, - * n: Array, - * t: String, - * d: Array, - * q: Array<[Number, string]>, - * i: Array, - * f: Array, - * p: Array, - * b: Array<[Number, String]>, - * c: Array - * }} - */ + for (const [crate, crateCorpus] of rawSearchIndex) { // This object should have exactly the same set of fields as the "row" // object defined below. Your JavaScript runtime will thank you. @@ -3039,7 +2992,7 @@ ${item.displayPath}${name}\ const itemDescs = crateCorpus.d; // an array of (Number) the parent path index + 1 to `paths`, or 0 if none const itemParentIdxs = crateCorpus.i; - // an array of (Object | null) the type of the function, if any + // an array of (Array | 0) the type of the function, if any const itemFunctionSearchTypes = crateCorpus.f; // an array of (Number) indices for the deprecated items const deprecatedItems = new Set(crateCorpus.c); From 12784c31669d7c9e69b49aa5776f8a4e55c319a8 Mon Sep 17 00:00:00 2001 From: quininer Date: Thu, 9 Nov 2023 16:46:32 +0800 Subject: [PATCH 021/257] Add -Zuse-sync-unwind This flag specifies whether LLVM generates async unwind or sync unwind. --- compiler/rustc_codegen_llvm/src/allocator.rs | 3 ++- compiler/rustc_codegen_llvm/src/attributes.rs | 7 ++++--- compiler/rustc_session/src/options.rs | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 58b3aa438c51..ca3760297354 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -134,7 +134,8 @@ fn create_wrapper_function( llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } if tcx.sess.must_emit_unwind_tables() { - let uwtable = attributes::uwtable_attr(llcx); + let uwtable = + attributes::uwtable_attr(llcx, tcx.sess.opts.unstable_opts.use_sync_unwind); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 3cc33b834340..0adf85cdda06 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -95,11 +95,12 @@ pub fn sanitize_attrs<'ll>( /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. #[inline] -pub fn uwtable_attr(llcx: &llvm::Context) -> &Attribute { +pub fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option) -> &Attribute { // NOTE: We should determine if we even need async unwind tables, as they // take have more overhead and if we can use sync unwind tables we // probably should. - llvm::CreateUWTableAttr(llcx, true) + let async_unwind = !use_sync_unwind.unwrap_or(false); + llvm::CreateUWTableAttr(llcx, async_unwind) } pub fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { @@ -333,7 +334,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( // You can also find more info on why Windows always requires uwtables here: // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 if cx.sess().must_emit_unwind_tables() { - to_add.push(uwtable_attr(cx.llcx)); + to_add.push(uwtable_attr(cx.llcx, cx.sess().opts.unstable_opts.use_sync_unwind)); } if cx.sess().opts.unstable_opts.profile_sample_use.is_some() { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 8274fd05bc05..9be97dd7cab2 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1991,6 +1991,8 @@ written to standard error output)"), "adds unstable command line options to rustc interface (default: no)"), use_ctors_section: Option = (None, parse_opt_bool, [TRACKED], "use legacy .ctors section for initializers rather than .init_array"), + use_sync_unwind: Option = (None, parse_opt_bool, [TRACKED], + "Generate sync unwind tables instead of async unwind tables (default: no)"), validate_mir: bool = (false, parse_bool, [UNTRACKED], "validate MIR after each transformation"), #[rustc_lint_opt_deny_field_access("use `Session::verbose_internals` instead of this field")] From 86b9550811b359a0af7a93523fbd14c5cdcc635f Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 30 Dec 2023 20:56:59 -0700 Subject: [PATCH 022/257] rustdoc-search: tighter encoding for `f` index Two optimizations for the function signature search: * Instead of using JSON arrays, like `[1,20]`, it uses VLQ hex with no commas, like `[aAd]`. * This also adds backrefs: if you have more than one function with exactly the same signature, it'll not only store it once, it'll *decode* it once, and store in the typeIdMap only once. Size change ----------- standard library ```console $ du -bs search-index-old.js search-index-new.js 4976370 search-index-old.js 4404391 search-index-new.js ``` ((4976370-4404391)/4404391)*100% = 12.9% Benchmarks are similarly shrunk: ```console $ du -hs tmp/{arti,cortex-m,sqlx,stm32f4,ripgrep}/toolchain_{old,new}/doc/search-index.js 10555067 tmp/arti/toolchain_old/doc/search-index.js 8921236 tmp/arti/toolchain_new/doc/search-index.js 77018 tmp/cortex-m/toolchain_old/doc/search-index.js 66676 tmp/cortex-m/toolchain_new/doc/search-index.js 2876330 tmp/sqlx/toolchain_old/doc/search-index.js 2436812 tmp/sqlx/toolchain_new/doc/search-index.js 63632890 tmp/stm32f4/toolchain_old/doc/search-index.js 52337438 tmp/stm32f4/toolchain_new/doc/search-index.js 631150 tmp/ripgrep/toolchain_old/doc/search-index.js 541646 tmp/ripgrep/toolchain_new/doc/search-index.js ``` --- src/librustdoc/html/render/mod.rs | 148 ++++++++++++++------- src/librustdoc/html/render/search_index.rs | 29 +--- src/librustdoc/html/static/js/search.js | 81 ++++++++--- 3 files changed, 171 insertions(+), 87 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 118c6eeb289b..345a47a21b8f 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -58,7 +58,7 @@ use rustc_span::{ symbol::{sym, Symbol}, BytePos, FileName, RealFileName, }; -use serde::ser::{SerializeMap, SerializeSeq}; +use serde::ser::SerializeMap; use serde::{Serialize, Serializer}; use crate::clean::{self, ItemId, RenderedLink, SelfTy}; @@ -123,44 +123,53 @@ pub(crate) struct IndexItem { } /// A type used for the search index. -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] pub(crate) struct RenderType { id: Option, generics: Option>, bindings: Option)>>, } -impl Serialize for RenderType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let id = match &self.id { - // 0 is a sentinel, everything else is one-indexed - None => 0, - // concrete type - Some(RenderTypeId::Index(idx)) if *idx >= 0 => idx + 1, - // generic type parameter - Some(RenderTypeId::Index(idx)) => *idx, - _ => panic!("must convert render types to indexes before serializing"), - }; +impl RenderType { + pub fn write_to_string(&self, string: &mut String) { if self.generics.is_some() || self.bindings.is_some() { - let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&id)?; - seq.serialize_element(self.generics.as_ref().map(Vec::as_slice).unwrap_or_default())?; - if self.bindings.is_some() { - seq.serialize_element( - self.bindings.as_ref().map(Vec::as_slice).unwrap_or_default(), - )?; + string.push('{'); + // 0 is a sentinel, everything else is one-indexed + match self.id { + Some(id) => id.write_to_string(string), + None => string.push('`'), } - seq.end() + string.push('{'); + for generic in &self.generics.as_ref().map(Vec::as_slice).unwrap_or_default()[..] { + generic.write_to_string(string); + } + string.push('}'); + if self.bindings.is_some() { + string.push('{'); + for binding in &self.bindings.as_ref().map(Vec::as_slice).unwrap_or_default()[..] { + string.push('{'); + binding.0.write_to_string(string); + string.push('{'); + for constraint in &binding.1[..] { + constraint.write_to_string(string); + } + string.push('}'); + string.push('}'); + } + string.push('}'); + } + string.push('}'); } else { - id.serialize(serializer) + // 0 is a sentinel, everything else is one-indexed + match self.id { + Some(id) => id.write_to_string(string), + None => string.push('`'), + } } } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) enum RenderTypeId { DefId(DefId), Primitive(clean::PrimitiveType), @@ -168,36 +177,50 @@ pub(crate) enum RenderTypeId { Index(isize), } -impl Serialize for RenderTypeId { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let id = match &self { +impl RenderTypeId { + pub fn write_to_string(&self, string: &mut String) { + // (sign, value) + let (sign, id): (bool, u32) = match &self { // 0 is a sentinel, everything else is one-indexed // concrete type - RenderTypeId::Index(idx) if *idx >= 0 => idx + 1, + RenderTypeId::Index(idx) if *idx >= 0 => (false, (idx + 1isize).try_into().unwrap()), // generic type parameter - RenderTypeId::Index(idx) => *idx, + RenderTypeId::Index(idx) => (true, (-*idx).try_into().unwrap()), _ => panic!("must convert render types to indexes before serializing"), }; - id.serialize(serializer) + // zig-zag notation + let value: u32 = (id << 1) | (if sign { 1 } else { 0 }); + // encode + let mut shift: u32 = 28; + let mut mask: u32 = 0xF0_00_00_00; + while shift < 32 { + let hexit = (value & mask) >> shift; + if hexit != 0 || shift == 0 { + let hex = + char::try_from(if shift == 0 { '`' } else { '@' } as u32 + hexit).unwrap(); + string.push(hex); + } + shift = shift.wrapping_sub(4); + mask = mask >> 4; + } } } /// Full type of functions/methods in the search index. -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] pub(crate) struct IndexItemFunctionType { inputs: Vec, output: Vec, where_clause: Vec>, } -impl Serialize for IndexItemFunctionType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { +impl IndexItemFunctionType { + pub fn write_to_string<'a>( + &'a self, + string: &mut String, + backref_queue: &mut VecDeque<&'a IndexItemFunctionType>, + ) { + assert!(backref_queue.len() < 16); // If we couldn't figure out a type, just write `0`. let has_missing = self .inputs @@ -205,33 +228,58 @@ impl Serialize for IndexItemFunctionType { .chain(self.output.iter()) .any(|i| i.id.is_none() && i.generics.is_none()); if has_missing { - 0.serialize(serializer) + string.push('`'); + } else if let Some(idx) = backref_queue.iter().position(|other| *other == self) { + string.push( + char::try_from('0' as u32 + u32::try_from(idx).unwrap()) + .expect("last possible value is '?'"), + ); } else { - let mut seq = serializer.serialize_seq(None)?; + backref_queue.push_front(self); + if backref_queue.len() >= 16 { + backref_queue.pop_back(); + } + string.push('{'); match &self.inputs[..] { [one] if one.generics.is_none() && one.bindings.is_none() => { - seq.serialize_element(one)? + one.write_to_string(string); + } + _ => { + string.push('{'); + for item in &self.inputs[..] { + item.write_to_string(string); + } + string.push('}'); } - _ => seq.serialize_element(&self.inputs)?, } match &self.output[..] { [] if self.where_clause.is_empty() => {} [one] if one.generics.is_none() && one.bindings.is_none() => { - seq.serialize_element(one)? + one.write_to_string(string); + } + _ => { + string.push('{'); + for item in &self.output[..] { + item.write_to_string(string); + } + string.push('}'); } - _ => seq.serialize_element(&self.output)?, } for constraint in &self.where_clause { if let [one] = &constraint[..] && one.generics.is_none() && one.bindings.is_none() { - seq.serialize_element(one)?; + one.write_to_string(string); } else { - seq.serialize_element(constraint)?; + string.push('{'); + for item in &constraint[..] { + item.write_to_string(string); + } + string.push('}'); } } - seq.end() + string.push('}'); } } } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index a1029320d2d2..e49df400c83e 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -1,5 +1,5 @@ use std::collections::hash_map::Entry; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, VecDeque}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_middle::ty::TyCtxt; @@ -409,9 +409,11 @@ pub(crate) fn build_index<'tcx>( let mut full_paths = Vec::with_capacity(self.items.len()); let mut descriptions = Vec::with_capacity(self.items.len()); let mut parents = Vec::with_capacity(self.items.len()); - let mut functions = Vec::with_capacity(self.items.len()); + let mut functions = String::with_capacity(self.items.len()); let mut deprecated = Vec::with_capacity(self.items.len()); + let mut backref_queue = VecDeque::new(); + for (index, item) in self.items.iter().enumerate() { let n = item.ty as u8; let c = char::try_from(n + b'A').expect("item types must fit in ASCII"); @@ -434,27 +436,10 @@ pub(crate) fn build_index<'tcx>( full_paths.push((index, &item.path)); } - // Fake option to get `0` out as a sentinel instead of `null`. - // We want to use `0` because it's three less bytes. - enum FunctionOption<'a> { - Function(&'a IndexItemFunctionType), - None, + match &item.search_type { + Some(ty) => ty.write_to_string(&mut functions, &mut backref_queue), + None => functions.push('`'), } - impl<'a> Serialize for FunctionOption<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - FunctionOption::None => 0.serialize(serializer), - FunctionOption::Function(ty) => ty.serialize(serializer), - } - } - } - functions.push(match &item.search_type { - Some(ty) => FunctionOption::Function(ty), - None => FunctionOption::None, - }); if item.deprecation.is_some() { deprecated.push(index); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 2df4f7020dfb..6fb92d8fbb12 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2767,19 +2767,65 @@ ${item.displayPath}${name}\ * The raw function search type format is generated using serde in * librustdoc/html/render/mod.rs: impl Serialize for IndexItemFunctionType * - * @param {RawFunctionSearchType} functionSearchType + * @param {{ + * string: string, + * offset: number, + * backrefQueue: FunctionSearchType[] + * }} itemFunctionDecoder * @param {Array<{name: string, ty: number}>} lowercasePaths * @param {Map} * * @return {null|FunctionSearchType} */ - function buildFunctionSearchType(functionSearchType, lowercasePaths) { - const INPUTS_DATA = 0; - const OUTPUT_DATA = 1; - // `0` is used as a sentinel because it's fewer bytes than `null` - if (functionSearchType === 0) { + function buildFunctionSearchType(itemFunctionDecoder, lowercasePaths) { + const c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); + itemFunctionDecoder.offset += 1; + const [zero, ua, la, ob, cb] = ["0", "@", "`", "{", "}"].map(c => c.charCodeAt(0)); + // `` ` `` is used as a sentinel because it's fewer bytes than `null`, and decodes to zero + // `0` is a backref + if (c === la) { return null; } + // sixteen characters after "0" are backref + if (c >= zero && c < ua) { + return itemFunctionDecoder.backrefQueue[c - zero]; + } + if (c !== ob) { + throw ["Unexpected ", c, " in function: expected ", "{", "; this is a bug"]; + } + // call after consuming `{` + function decodeList() { + let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); + const ret = []; + while (c !== cb) { + ret.push(decode()); + c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); + } + itemFunctionDecoder.offset += 1; // eat cb + return ret; + } + // consumes and returns a list or integer + function decode() { + let n = 0; + let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); + if (c === ob) { + itemFunctionDecoder.offset += 1; + return decodeList(); + } + while (c < la) { + n = (n << 4) | (c & 0xF); + itemFunctionDecoder.offset += 1; + c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); + } + // last character >= la + n = (n << 4) | (c & 0xF); + const [sign, value] = [n & 1, n >> 1]; + itemFunctionDecoder.offset += 1; + return sign ? -value : value; + } + const functionSearchType = decodeList(); + const INPUTS_DATA = 0; + const OUTPUT_DATA = 1; let inputs, output; if (typeof functionSearchType[INPUTS_DATA] === "number") { inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; @@ -2808,9 +2854,14 @@ ${item.displayPath}${name}\ ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); } - return { + const ret = { inputs, output, where_clause, }; + itemFunctionDecoder.backrefQueue.unshift(ret); + if (itemFunctionDecoder.backrefQueue.length >= 16) { + itemFunctionDecoder.backrefQueue.pop(); + } + return ret; } /** @@ -2992,8 +3043,12 @@ ${item.displayPath}${name}\ const itemDescs = crateCorpus.d; // an array of (Number) the parent path index + 1 to `paths`, or 0 if none const itemParentIdxs = crateCorpus.i; - // an array of (Array | 0) the type of the function, if any - const itemFunctionSearchTypes = crateCorpus.f; + // a string representing the list of function types + const itemFunctionDecoder = { + string: crateCorpus.f, + offset: 0, + backrefQueue: [], + }; // an array of (Number) indices for the deprecated items const deprecatedItems = new Set(crateCorpus.c); // an array of (Number) indices for the deprecated items @@ -3041,12 +3096,8 @@ ${item.displayPath}${name}\ word = itemNames[i].toLowerCase(); } const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; - let type = null; - if (itemFunctionSearchTypes[i] !== 0) { - type = buildFunctionSearchType( - itemFunctionSearchTypes[i], - lowercasePaths - ); + const type = buildFunctionSearchType(itemFunctionDecoder, lowercasePaths); + if (type !== null) { if (type) { const fp = functionTypeFingerprint.subarray(id * 4, (id + 1) * 4); const fps = new Set(); From 6ed37bdc4260bdf64acf0cb2c728d6724c94ac3d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 31 Dec 2023 19:35:32 +0000 Subject: [PATCH 023/257] Avoid specialization for the Span Encodable and Decodable impls --- Cargo.lock | 1 + compiler/rustc_abi/src/lib.rs | 14 ++-- compiler/rustc_ast/src/tokenstream.rs | 8 +-- .../rustc_data_structures/src/sorted_map.rs | 2 +- compiler/rustc_data_structures/src/svh.rs | 2 +- compiler/rustc_data_structures/src/unord.rs | 6 +- compiler/rustc_index/src/bit_set.rs | 8 +-- compiler/rustc_macros/src/lib.rs | 2 + compiler/rustc_macros/src/serialize.rs | 16 +++++ compiler/rustc_metadata/src/rmeta/decoder.rs | 18 ++--- compiler/rustc_metadata/src/rmeta/encoder.rs | 22 +++---- .../rustc_middle/src/query/on_disk_cache.rs | 65 ++++++++++--------- compiler/rustc_serialize/tests/opaque.rs | 16 ++--- compiler/rustc_span/src/lib.rs | 46 +++++++++---- compiler/rustc_type_ir/Cargo.toml | 2 + compiler/rustc_type_ir/src/codec.rs | 6 +- 16 files changed, 140 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8192e333fe9..1948e6c2e5ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4644,6 +4644,7 @@ dependencies = [ "rustc_index", "rustc_macros", "rustc_serialize", + "rustc_span", "smallvec", ] diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 549927d58987..ea194e10defd 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -16,7 +16,7 @@ use rustc_data_structures::stable_hasher::StableOrd; #[cfg(feature = "nightly")] use rustc_macros::HashStable_Generic; #[cfg(feature = "nightly")] -use rustc_macros::{Decodable, Encodable}; +use rustc_macros::{Decodable_Generic, Encodable_Generic}; #[cfg(feature = "nightly")] use std::iter::Step; @@ -30,7 +30,7 @@ pub use layout::LayoutCalculator; pub trait HashStableContext {} #[derive(Clone, Copy, PartialEq, Eq, Default)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))] +#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] pub struct ReprFlags(u8); bitflags! { @@ -52,7 +52,7 @@ bitflags! { rustc_data_structures::external_bitflags_debug! { ReprFlags } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))] +#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] pub enum IntegerType { /// Pointer-sized integer type, i.e. `isize` and `usize`. The field shows signedness, e.g. /// `Pointer(true)` means `isize`. @@ -73,7 +73,7 @@ impl IntegerType { /// Represents the repr options provided by the user. #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))] +#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] pub struct ReprOptions { pub int: Option, pub align: Option, @@ -412,7 +412,7 @@ impl FromStr for Endian { /// Size of a type in bytes. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))] +#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] pub struct Size { raw: u64, } @@ -636,7 +636,7 @@ impl Step for Size { /// Alignment of a type in bytes (always a power of two). #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))] +#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] pub struct Align { pow2: u8, } @@ -777,7 +777,7 @@ impl AbiAndPrefAlign { /// Integers, also used for enum discriminants. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))] +#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] pub enum Integer { I8, I16, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 4c0c496584eb..503862e6f018 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -21,8 +21,8 @@ use crate::AttrVec; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{self, Lrc}; use rustc_macros::HashStable_Generic; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use rustc_span::{sym, Span, Symbol, DUMMY_SP}; +use rustc_serialize::{Decodable, Encodable}; +use rustc_span::{sym, Span, SpanDecoder, SpanEncoder, Symbol, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::borrow::Cow; @@ -158,14 +158,14 @@ impl fmt::Debug for LazyAttrTokenStream { } } -impl Encodable for LazyAttrTokenStream { +impl Encodable for LazyAttrTokenStream { fn encode(&self, s: &mut S) { // Used by AST json printing. Encodable::encode(&self.to_attr_token_stream(), s); } } -impl Decodable for LazyAttrTokenStream { +impl Decodable for LazyAttrTokenStream { fn decode(_d: &mut D) -> Self { panic!("Attempted to decode LazyAttrTokenStream"); } diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index ed2e558bffa6..1436628139fd 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -16,7 +16,7 @@ pub use index_map::SortedIndexMultiMap; /// stores data in a more compact way. It also supports accessing contiguous /// ranges of elements as a slice, and slices of already sorted elements can be /// inserted efficiently. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable_Generic, Decodable_Generic)] pub struct SortedMap { data: Vec<(K, V)>, } diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index 71679086f16c..1cfc9fecd47d 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -10,7 +10,7 @@ use std::fmt; use crate::stable_hasher; -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable_Generic, Decodable_Generic, Hash)] pub struct Svh { hash: Fingerprint, } diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index 47c56eba7adc..9f8ddbd4256e 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -185,7 +185,7 @@ trait UnordCollection {} /// /// See [MCP 533](https://github.com/rust-lang/compiler-team/issues/533) /// for more information. -#[derive(Debug, Eq, PartialEq, Clone, Encodable, Decodable)] +#[derive(Debug, Eq, PartialEq, Clone, Encodable_Generic, Decodable_Generic)] pub struct UnordSet { inner: FxHashSet, } @@ -362,7 +362,7 @@ impl> HashStable for UnordSet { /// /// See [MCP 533](https://github.com/rust-lang/compiler-team/issues/533) /// for more information. -#[derive(Debug, Eq, PartialEq, Clone, Encodable, Decodable)] +#[derive(Debug, Eq, PartialEq, Clone, Encodable_Generic, Decodable_Generic)] pub struct UnordMap { inner: FxHashMap, } @@ -558,7 +558,7 @@ impl, V: HashStable> HashStable fo /// /// See [MCP 533](https://github.com/rust-lang/compiler-team/issues/533) /// for more information. -#[derive(Default, Debug, Eq, PartialEq, Clone, Encodable, Decodable)] +#[derive(Default, Debug, Eq, PartialEq, Clone, Encodable_Generic, Decodable_Generic)] pub struct UnordBag { inner: Vec, } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index d730ef58deba..cba02548eb54 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -10,7 +10,7 @@ use arrayvec::ArrayVec; use smallvec::{smallvec, SmallVec}; #[cfg(feature = "nightly")] -use rustc_macros::{Decodable, Encodable}; +use rustc_macros::{Decodable_Generic, Encodable_Generic}; use crate::{Idx, IndexVec}; @@ -112,7 +112,7 @@ macro_rules! bit_relations_inherent_impls { /// to or greater than the domain size. All operations that involve two bitsets /// will panic if the bitsets have differing domain sizes. /// -#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))] +#[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))] #[derive(Eq, PartialEq, Hash)] pub struct BitSet { domain_size: usize, @@ -1590,7 +1590,7 @@ impl From> for GrowableBitSet { /// /// All operations that involve a row and/or column index will panic if the /// index exceeds the relevant bound. -#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))] +#[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))] #[derive(Clone, Eq, PartialEq, Hash)] pub struct BitMatrix { num_rows: usize, @@ -2020,7 +2020,7 @@ impl std::fmt::Debug for FiniteBitSet { /// A fixed-sized bitset type represented by an integer type. Indices outwith than the range /// representable by `T` are considered set. -#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))] +#[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))] #[derive(Copy, Clone, Eq, PartialEq)] pub struct FiniteBitSet(pub T); diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index f558b74be9a9..f5d942b924e6 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -56,6 +56,8 @@ decl_derive!( hash_stable::hash_stable_no_context_derive ); +decl_derive!([Decodable_Generic] => serialize::decodable_generic_derive); +decl_derive!([Encodable_Generic] => serialize::encodable_generic_derive); decl_derive!([Decodable] => serialize::decodable_derive); decl_derive!([Encodable] => serialize::encodable_derive); decl_derive!([TyDecodable] => serialize::type_decodable_derive); diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 047066ac6815..9ca7ce09ba6c 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -31,6 +31,14 @@ pub fn meta_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: } pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + let decoder_ty = quote! { __D }; + s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_span::SpanDecoder}); + s.add_bounds(synstructure::AddBounds::Generics); + + decodable_body(s, decoder_ty) +} + +pub fn decodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let decoder_ty = quote! { __D }; s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_serialize::Decoder}); s.add_bounds(synstructure::AddBounds::Generics); @@ -129,6 +137,14 @@ pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: } pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + let encoder_ty = quote! { __E }; + s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder}); + s.add_bounds(synstructure::AddBounds::Generics); + + encodable_body(s, encoder_ty, false) +} + +pub fn encodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let encoder_ty = quote! { __E }; s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder}); s.add_bounds(synstructure::AddBounds::Generics); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 2de29db9e5c8..2dffd5446a31 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -26,7 +26,7 @@ use rustc_serialize::{Decodable, Decoder}; use rustc_session::cstore::{CrateSource, ExternCrate}; use rustc_session::Session; use rustc_span::symbol::kw; -use rustc_span::{BytePos, Pos, SpanData, SyntaxContext, DUMMY_SP}; +use rustc_span::{BytePos, Pos, SpanData, SpanDecoder, SyntaxContext, DUMMY_SP}; use proc_macro::bridge::client::ProcMacro; use std::iter::TrustedLen; @@ -505,22 +505,22 @@ impl<'a, 'tcx> Decodable> for ExpnId { } } -impl<'a, 'tcx> Decodable> for Span { - fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Span { - let start = decoder.position(); - let tag = SpanTag(decoder.peek_byte()); +impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> { + fn decode_span(&mut self) -> Span { + let start = self.position(); + let tag = SpanTag(self.peek_byte()); let data = if tag.kind() == SpanKind::Indirect { // Skip past the tag we just peek'd. - decoder.read_u8(); - let offset_or_position = decoder.read_usize(); + self.read_u8(); + let offset_or_position = self.read_usize(); let position = if tag.is_relative_offset() { start - offset_or_position } else { offset_or_position }; - decoder.with_position(position, SpanData::decode) + self.with_position(position, SpanData::decode) } else { - SpanData::decode(decoder) + SpanData::decode(self) }; Span::new(data.lo, data.hi, data.ctxt, data.parent) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index aca7a66596e6..dbd82d3ab79b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -27,7 +27,7 @@ use rustc_session::config::{CrateType, OptLevel}; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::symbol::sym; use rustc_span::{ - ExternalSource, FileName, SourceFile, SpanData, StableSourceFileId, SyntaxContext, + ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, SyntaxContext, }; use std::borrow::Borrow; use std::collections::hash_map::Entry; @@ -166,29 +166,29 @@ impl<'a, 'tcx> Encodable> for ExpnId { } } -impl<'a, 'tcx> Encodable> for Span { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { - match s.span_shorthands.entry(*self) { +impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { + fn encode_span(&mut self, span: Span) { + match self.span_shorthands.entry(span) { Entry::Occupied(o) => { // If an offset is smaller than the absolute position, we encode with the offset. // This saves space since smaller numbers encode in less bits. let last_location = *o.get(); // This cannot underflow. Metadata is written with increasing position(), so any // previously saved offset must be smaller than the current position. - let offset = s.opaque.position() - last_location; + let offset = self.opaque.position() - last_location; if offset < last_location { - SpanTag::indirect(true).encode(s); - offset.encode(s); + SpanTag::indirect(true).encode(self); + offset.encode(self); } else { - SpanTag::indirect(false).encode(s); - last_location.encode(s); + SpanTag::indirect(false).encode(self); + last_location.encode(self); } } Entry::Vacant(v) => { - let position = s.opaque.position(); + let position = self.opaque.position(); v.insert(position); // Data is encoded with a SpanTag prefix (see below). - self.data().encode(s); + span.data().encode(self); } } } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 0577d22d8508..f8a34e05eb05 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -22,7 +22,8 @@ use rustc_span::hygiene::{ }; use rustc_span::source_map::SourceMap; use rustc_span::{ - BytePos, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span, StableSourceFileId, + BytePos, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span, SpanDecoder, SpanEncoder, + StableSourceFileId, }; use rustc_span::{CachingSourceMapView, Symbol}; use std::collections::hash_map::Entry; @@ -648,19 +649,19 @@ impl<'a, 'tcx> Decodable> for ExpnId { } } -impl<'a, 'tcx> Decodable> for Span { - fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self { - let ctxt = SyntaxContext::decode(decoder); - let parent = Option::::decode(decoder); - let tag: u8 = Decodable::decode(decoder); +impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { + fn decode_span(&mut self) -> Span { + let ctxt = SyntaxContext::decode(self); + let parent = Option::::decode(self); + let tag: u8 = Decodable::decode(self); if tag == TAG_PARTIAL_SPAN { return Span::new(BytePos(0), BytePos(0), ctxt, parent); } else if tag == TAG_RELATIVE_SPAN { - let dlo = u32::decode(decoder); - let dto = u32::decode(decoder); + let dlo = u32::decode(self); + let dto = u32::decode(self); - let enclosing = decoder.tcx.source_span_untracked(parent.unwrap()).data_untracked(); + let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked(); let span = Span::new( enclosing.lo + BytePos::from_u32(dlo), enclosing.lo + BytePos::from_u32(dto), @@ -673,12 +674,12 @@ impl<'a, 'tcx> Decodable> for Span { debug_assert_eq!(tag, TAG_FULL_SPAN); } - let file_lo_index = SourceFileIndex::decode(decoder); - let line_lo = usize::decode(decoder); - let col_lo = RelativeBytePos::decode(decoder); - let len = BytePos::decode(decoder); + let file_lo_index = SourceFileIndex::decode(self); + let line_lo = usize::decode(self); + let col_lo = RelativeBytePos::decode(self); + let len = BytePos::decode(self); - let file_lo = decoder.file_index_to_file(file_lo_index); + let file_lo = self.file_index_to_file(file_lo_index); let lo = file_lo.lines()[line_lo - 1] + col_lo; let lo = file_lo.absolute_position(lo); let hi = lo + len; @@ -872,47 +873,47 @@ impl<'a, 'tcx> Encodable> for ExpnId { } } -impl<'a, 'tcx> Encodable> for Span { - fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { - let span_data = self.data_untracked(); - span_data.ctxt.encode(s); - span_data.parent.encode(s); +impl<'a, 'tcx> SpanEncoder for CacheEncoder<'a, 'tcx> { + fn encode_span(&mut self, span: Span) { + let span_data = span.data_untracked(); + span_data.ctxt.encode(self); + span_data.parent.encode(self); if span_data.is_dummy() { - return TAG_PARTIAL_SPAN.encode(s); + return TAG_PARTIAL_SPAN.encode(self); } if let Some(parent) = span_data.parent { - let enclosing = s.tcx.source_span_untracked(parent).data_untracked(); + let enclosing = self.tcx.source_span_untracked(parent).data_untracked(); if enclosing.contains(span_data) { - TAG_RELATIVE_SPAN.encode(s); - (span_data.lo - enclosing.lo).to_u32().encode(s); - (span_data.hi - enclosing.lo).to_u32().encode(s); + TAG_RELATIVE_SPAN.encode(self); + (span_data.lo - enclosing.lo).to_u32().encode(self); + (span_data.hi - enclosing.lo).to_u32().encode(self); return; } } - let pos = s.source_map.byte_pos_to_line_and_col(span_data.lo); + let pos = self.source_map.byte_pos_to_line_and_col(span_data.lo); let partial_span = match &pos { Some((file_lo, _, _)) => !file_lo.contains(span_data.hi), None => true, }; if partial_span { - return TAG_PARTIAL_SPAN.encode(s); + return TAG_PARTIAL_SPAN.encode(self); } let (file_lo, line_lo, col_lo) = pos.unwrap(); let len = span_data.hi - span_data.lo; - let source_file_index = s.source_file_index(file_lo); + let source_file_index = self.source_file_index(file_lo); - TAG_FULL_SPAN.encode(s); - source_file_index.encode(s); - line_lo.encode(s); - col_lo.encode(s); - len.encode(s); + TAG_FULL_SPAN.encode(self); + source_file_index.encode(self); + line_lo.encode(self); + col_lo.encode(self); + len.encode(self); } } diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs index 861091688bb2..45ff85f38d2b 100644 --- a/compiler/rustc_serialize/tests/opaque.rs +++ b/compiler/rustc_serialize/tests/opaque.rs @@ -1,12 +1,12 @@ #![allow(rustc::internal)] -use rustc_macros::{Decodable, Encodable}; -use rustc_serialize::opaque::{MemDecoder, FileEncoder}; +use rustc_macros::{Decodable_Generic, Encodable_Generic}; +use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Encodable}; use std::fmt::Debug; use std::fs; -#[derive(PartialEq, Clone, Debug, Encodable, Decodable)] +#[derive(PartialEq, Clone, Debug, Encodable_Generic, Decodable_Generic)] struct Struct { a: (), b: u8, @@ -209,7 +209,7 @@ fn test_struct() { }]); } -#[derive(PartialEq, Clone, Debug, Encodable, Decodable)] +#[derive(PartialEq, Clone, Debug, Encodable_Generic, Decodable_Generic)] enum Enum { Variant1, Variant2(usize, u32), @@ -258,7 +258,7 @@ fn test_tuples() { #[test] fn test_unit_like_struct() { - #[derive(Encodable, Decodable, PartialEq, Debug)] + #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)] struct UnitLikeStruct; check_round_trip(vec![UnitLikeStruct]); @@ -266,7 +266,7 @@ fn test_unit_like_struct() { #[test] fn test_box() { - #[derive(Encodable, Decodable, PartialEq, Debug)] + #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)] struct A { foo: Box<[bool]>, } @@ -279,12 +279,12 @@ fn test_box() { fn test_cell() { use std::cell::{Cell, RefCell}; - #[derive(Encodable, Decodable, PartialEq, Debug)] + #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)] struct A { baz: isize, } - #[derive(Encodable, Decodable, PartialEq, Debug)] + #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)] struct B { foo: Cell, bar: RefCell, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 8f64eed9a870..0eafbae6e82b 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -35,6 +35,8 @@ #![feature(rustdoc_internals)] // tidy-alphabetical-end +extern crate self as rustc_span; + #[macro_use] extern crate rustc_macros; @@ -43,6 +45,7 @@ extern crate tracing; use rustc_data_structures::{outline, AtomicRef}; use rustc_macros::HashStable_Generic; +use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; mod caching_source_map_view; @@ -1016,22 +1019,43 @@ impl Default for Span { } } -impl Encodable for Span { - default fn encode(&self, s: &mut E) { - let span = self.data(); - span.lo.encode(s); - span.hi.encode(s); +pub trait SpanEncoder: Encoder { + fn encode_span(&mut self, span: Span); +} + +impl SpanEncoder for FileEncoder { + fn encode_span(&mut self, span: Span) { + let span = span.data(); + span.lo.encode(self); + span.hi.encode(self); } } -impl Decodable for Span { - default fn decode(s: &mut D) -> Span { - let lo = Decodable::decode(s); - let hi = Decodable::decode(s); + +impl Encodable for Span { + fn encode(&self, s: &mut E) { + s.encode_span(*self); + } +} + +pub trait SpanDecoder: Decoder { + fn decode_span(&mut self) -> Span; +} + +impl SpanDecoder for MemDecoder<'_> { + fn decode_span(&mut self) -> Span { + let lo = Decodable::decode(self); + let hi = Decodable::decode(self); Span::new(lo, hi, SyntaxContext::root(), None) } } +impl Decodable for Span { + fn decode(s: &mut D) -> Span { + s.decode_span() + } +} + /// Insert `source_map` into the session globals for the duration of the /// closure's execution. pub fn set_source_map T>(source_map: Lrc, f: F) -> T { @@ -1360,7 +1384,7 @@ impl Clone for SourceFile { } } -impl Encodable for SourceFile { +impl Encodable for SourceFile { fn encode(&self, s: &mut S) { self.name.encode(s); self.src_hash.encode(s); @@ -1434,7 +1458,7 @@ impl Encodable for SourceFile { } } -impl Decodable for SourceFile { +impl Decodable for SourceFile { fn decode(d: &mut D) -> SourceFile { let name: FileName = Decodable::decode(d); let src_hash: SourceFileHash = Decodable::decode(d); diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 38f0eb821801..3a94256f402f 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -10,6 +10,7 @@ derivative = "2.2.0" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } +rustc_span = { path = "../rustc_span", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } smallvec = { version = "1.8.1" } # tidy-alphabetical-end @@ -20,6 +21,7 @@ nightly = [ "smallvec/may_dangle", "smallvec/union", "rustc_index/nightly", + "rustc_span", "rustc_serialize", "rustc_data_structures", "rustc_macros", diff --git a/compiler/rustc_type_ir/src/codec.rs b/compiler/rustc_type_ir/src/codec.rs index 2fbc8f76fa4f..71f9eb0ef8a8 100644 --- a/compiler/rustc_type_ir/src/codec.rs +++ b/compiler/rustc_type_ir/src/codec.rs @@ -1,7 +1,7 @@ use crate::{Interner, PredicateKind}; use rustc_data_structures::fx::FxHashMap; -use rustc_serialize::{Decoder, Encoder}; +use rustc_span::{SpanDecoder, SpanEncoder}; /// The shorthand encoding uses an enum's variant index `usize` /// and is offset by this value so it never matches a real variant. @@ -22,7 +22,7 @@ pub trait RefDecodable<'tcx, D: TyDecoder> { fn decode(d: &mut D) -> &'tcx Self; } -pub trait TyEncoder: Encoder { +pub trait TyEncoder: SpanEncoder { type I: Interner; const CLEAR_CROSS_CRATE: bool; @@ -35,7 +35,7 @@ pub trait TyEncoder: Encoder { fn encode_alloc_id(&mut self, alloc_id: &::AllocId); } -pub trait TyDecoder: Decoder { +pub trait TyDecoder: SpanDecoder { type I: Interner; const CLEAR_CROSS_CRATE: bool; From 8d598b0d581872c5222399f500dd5f9f3f3aa1c3 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:27:35 +0000 Subject: [PATCH 024/257] Remove almost all uses of specialization from the metadata encoding code --- compiler/rustc_metadata/src/rmeta/decoder.rs | 106 ++++++----- compiler/rustc_metadata/src/rmeta/encoder.rs | 99 +++++------ .../rustc_middle/src/query/on_disk_cache.rs | 164 ++++++++---------- compiler/rustc_span/src/def_id.rs | 47 +---- compiler/rustc_span/src/hygiene.rs | 30 +--- compiler/rustc_span/src/lib.rs | 137 ++++++++++++++- compiler/rustc_span/src/symbol.rs | 14 -- 7 files changed, 307 insertions(+), 290 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 2dffd5446a31..d20f4bc7a941 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -409,21 +409,6 @@ impl<'a, 'tcx> TyDecoder for DecodeContext<'a, 'tcx> { } } -impl<'a, 'tcx> Decodable> for CrateNum { - #[inline] - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> CrateNum { - let cnum = CrateNum::from_u32(d.read_u32()); - d.map_encoded_cnum_to_current(cnum) - } -} - -impl<'a, 'tcx> Decodable> for DefIndex { - #[inline] - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> DefIndex { - DefIndex::from_u32(d.read_u32()) - } -} - impl<'a, 'tcx> Decodable> for ExpnIndex { #[inline] fn decode(d: &mut DecodeContext<'a, 'tcx>) -> ExpnIndex { @@ -439,11 +424,24 @@ impl<'a, 'tcx> Decodable> for ast::AttrId { } } -impl<'a, 'tcx> Decodable> for SyntaxContext { - fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> SyntaxContext { - let cdata = decoder.cdata(); +impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> { + fn decode_crate_num(&mut self) -> CrateNum { + let cnum = CrateNum::from_u32(self.read_u32()); + self.map_encoded_cnum_to_current(cnum) + } - let Some(sess) = decoder.sess else { + fn decode_def_index(&mut self) -> DefIndex { + DefIndex::from_u32(self.read_u32()) + } + + fn decode_def_id(&mut self) -> DefId { + DefId { krate: Decodable::decode(self), index: Decodable::decode(self) } + } + + fn decode_syntax_context(&mut self) -> SyntaxContext { + let cdata = self.cdata(); + + let Some(sess) = self.sess else { bug!( "Cannot decode SyntaxContext without Session.\ You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`." @@ -451,7 +449,7 @@ impl<'a, 'tcx> Decodable> for SyntaxContext { }; let cname = cdata.root.name(); - rustc_span::hygiene::decode_syntax_context(decoder, &cdata.hygiene_context, |_, id| { + rustc_span::hygiene::decode_syntax_context(self, &cdata.hygiene_context, |_, id| { debug!("SpecializedDecoder: decoding {}", id); cdata .root @@ -461,21 +459,19 @@ impl<'a, 'tcx> Decodable> for SyntaxContext { .decode((cdata, sess)) }) } -} -impl<'a, 'tcx> Decodable> for ExpnId { - fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> ExpnId { - let local_cdata = decoder.cdata(); + fn decode_expn_id(&mut self) -> ExpnId { + let local_cdata = self.cdata(); - let Some(sess) = decoder.sess else { + let Some(sess) = self.sess else { bug!( "Cannot decode ExpnId without Session. \ You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`." ); }; - let cnum = CrateNum::decode(decoder); - let index = u32::decode(decoder); + let cnum = CrateNum::decode(self); + let index = u32::decode(self); let expn_id = rustc_span::hygiene::decode_expn_id(cnum, index, |expn_id| { let ExpnId { krate: cnum, local_id: index } = expn_id; @@ -503,9 +499,7 @@ impl<'a, 'tcx> Decodable> for ExpnId { }); expn_id } -} -impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> { fn decode_span(&mut self) -> Span { let start = self.position(); let tag = SpanTag(self.peek_byte()); @@ -524,6 +518,32 @@ impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> { }; Span::new(data.lo, data.hi, data.ctxt, data.parent) } + + fn decode_symbol(&mut self) -> Symbol { + let tag = self.read_u8(); + + match tag { + SYMBOL_STR => { + let s = self.read_str(); + Symbol::intern(s) + } + SYMBOL_OFFSET => { + // read str offset + let pos = self.read_usize(); + + // move to str offset and read + self.opaque.with_position(pos, |d| { + let s = d.read_str(); + Symbol::intern(s) + }) + } + SYMBOL_PREINTERNED => { + let symbol_index = self.read_u32(); + Symbol::new_from_decoded(symbol_index) + } + _ => unreachable!(), + } + } } impl<'a, 'tcx> Decodable> for SpanData { @@ -631,34 +651,6 @@ impl<'a, 'tcx> Decodable> for SpanData { } } -impl<'a, 'tcx> Decodable> for Symbol { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { - let tag = d.read_u8(); - - match tag { - SYMBOL_STR => { - let s = d.read_str(); - Symbol::intern(s) - } - SYMBOL_OFFSET => { - // read str offset - let pos = d.read_usize(); - - // move to str offset and read - d.opaque.with_position(pos, |d| { - let s = d.read_str(); - Symbol::intern(s) - }) - } - SYMBOL_PREINTERNED => { - let symbol_index = d.read_u32(); - Symbol::new_from_decoded(symbol_index) - } - _ => unreachable!(), - } - } -} - impl<'a, 'tcx> Decodable> for &'tcx [(ty::Clause<'tcx>, Span)] { fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { ty::codec::RefDecodable::decode(d) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index dbd82d3ab79b..2ed4385df085 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -125,48 +125,45 @@ impl<'a, 'tcx, I, T> Encodable> for LazyTable { } } -impl<'a, 'tcx> Encodable> for CrateNum { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { - if *self != LOCAL_CRATE && s.is_proc_macro { - panic!("Attempted to encode non-local CrateNum {self:?} for proc-macro crate"); - } - s.emit_u32(self.as_u32()); - } -} - -impl<'a, 'tcx> Encodable> for DefIndex { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { - s.emit_u32(self.as_u32()); - } -} - impl<'a, 'tcx> Encodable> for ExpnIndex { fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { s.emit_u32(self.as_u32()); } } -impl<'a, 'tcx> Encodable> for SyntaxContext { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { - rustc_span::hygiene::raw_encode_syntax_context(*self, s.hygiene_ctxt, s); +impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { + fn encode_crate_num(&mut self, crate_num: CrateNum) { + if crate_num != LOCAL_CRATE && self.is_proc_macro { + panic!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate"); + } + self.emit_u32(crate_num.as_u32()); } -} -impl<'a, 'tcx> Encodable> for ExpnId { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { - if self.krate == LOCAL_CRATE { + fn encode_def_index(&mut self, def_index: DefIndex) { + self.emit_u32(def_index.as_u32()); + } + + fn encode_def_id(&mut self, def_id: DefId) { + def_id.krate.encode(self); + def_id.index.encode(self); + } + + fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { + rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self); + } + + fn encode_expn_id(&mut self, expn_id: ExpnId) { + if expn_id.krate == LOCAL_CRATE { // We will only write details for local expansions. Non-local expansions will fetch // data from the corresponding crate's metadata. // FIXME(#43047) FIXME(#74731) We may eventually want to avoid relying on external // metadata from proc-macro crates. - s.hygiene_ctxt.schedule_expn_data_for_encoding(*self); + self.hygiene_ctxt.schedule_expn_data_for_encoding(expn_id); } - self.krate.encode(s); - self.local_id.encode(s); + expn_id.krate.encode(self); + expn_id.local_id.encode(self); } -} -impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { fn encode_span(&mut self, span: Span) { match self.span_shorthands.entry(span) { Entry::Occupied(o) => { @@ -192,6 +189,29 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { } } } + + fn encode_symbol(&mut self, symbol: Symbol) { + // if symbol preinterned, emit tag and symbol index + if symbol.is_preinterned() { + self.opaque.emit_u8(SYMBOL_PREINTERNED); + self.opaque.emit_u32(symbol.as_u32()); + } else { + // otherwise write it as string or as offset to it + match self.symbol_table.entry(symbol) { + Entry::Vacant(o) => { + self.opaque.emit_u8(SYMBOL_STR); + let pos = self.opaque.position(); + o.insert(pos); + self.emit_str(symbol.as_str()); + } + Entry::Occupied(o) => { + let x = *o.get(); + self.emit_u8(SYMBOL_OFFSET); + self.emit_usize(x); + } + } + } + } } impl<'a, 'tcx> Encodable> for SpanData { @@ -337,31 +357,6 @@ impl<'a, 'tcx> Encodable> for SpanData { } } -impl<'a, 'tcx> Encodable> for Symbol { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { - // if symbol preinterned, emit tag and symbol index - if self.is_preinterned() { - s.opaque.emit_u8(SYMBOL_PREINTERNED); - s.opaque.emit_u32(self.as_u32()); - } else { - // otherwise write it as string or as offset to it - match s.symbol_table.entry(*self) { - Entry::Vacant(o) => { - s.opaque.emit_u8(SYMBOL_STR); - let pos = s.opaque.position(); - o.insert(pos); - s.emit_str(self.as_str()); - } - Entry::Occupied(o) => { - let x = *o.get(); - s.emit_u8(SYMBOL_OFFSET); - s.emit_usize(x); - } - } - } - } -} - impl<'a, 'tcx> Encodable> for [u8] { fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) { Encoder::emit_usize(e, self.len()); diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index f8a34e05eb05..8d084f5f1311 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -583,10 +583,10 @@ impl<'a, 'tcx> Decodable> for Vec { } } -impl<'a, 'tcx> Decodable> for SyntaxContext { - fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self { - let syntax_contexts = decoder.syntax_contexts; - rustc_span::hygiene::decode_syntax_context(decoder, decoder.hygiene_context, |this, id| { +impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { + fn decode_syntax_context(&mut self) -> SyntaxContext { + let syntax_contexts = self.syntax_contexts; + rustc_span::hygiene::decode_syntax_context(self, self.hygiene_context, |this, id| { // This closure is invoked if we haven't already decoded the data for the `SyntaxContext` we are deserializing. // We look up the position of the associated `SyntaxData` and decode it. let pos = syntax_contexts.get(&id).unwrap(); @@ -596,11 +596,9 @@ impl<'a, 'tcx> Decodable> for SyntaxContext { }) }) } -} -impl<'a, 'tcx> Decodable> for ExpnId { - fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self { - let hash = ExpnHash::decode(decoder); + fn decode_expn_id(&mut self) -> ExpnId { + let hash = ExpnHash::decode(self); if hash.is_root() { return ExpnId::root(); } @@ -609,23 +607,23 @@ impl<'a, 'tcx> Decodable> for ExpnId { return expn_id; } - let krate = decoder.tcx.stable_crate_id_to_crate_num(hash.stable_crate_id()); + let krate = self.tcx.stable_crate_id_to_crate_num(hash.stable_crate_id()); let expn_id = if krate == LOCAL_CRATE { // We look up the position of the associated `ExpnData` and decode it. - let pos = decoder + let pos = self .expn_data .get(&hash) - .unwrap_or_else(|| panic!("Bad hash {:?} (map {:?})", hash, decoder.expn_data)); + .unwrap_or_else(|| panic!("Bad hash {:?} (map {:?})", hash, self.expn_data)); - let data: ExpnData = decoder - .with_position(pos.to_usize(), |decoder| decode_tagged(decoder, TAG_EXPN_DATA)); + let data: ExpnData = + self.with_position(pos.to_usize(), |decoder| decode_tagged(decoder, TAG_EXPN_DATA)); let expn_id = rustc_span::hygiene::register_local_expn_id(data, hash); #[cfg(debug_assertions)] { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; - let local_hash = decoder.tcx.with_stable_hashing_context(|mut hcx| { + let local_hash = self.tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); expn_id.expn_data().hash_stable(&mut hcx, &mut hasher); hasher.finish() @@ -635,9 +633,9 @@ impl<'a, 'tcx> Decodable> for ExpnId { expn_id } else { - let index_guess = decoder.foreign_expn_data[&hash]; - decoder.tcx.cstore_untracked().expn_hash_to_expn_id( - decoder.tcx.sess, + let index_guess = self.foreign_expn_data[&hash]; + self.tcx.cstore_untracked().expn_hash_to_expn_id( + self.tcx.sess, krate, index_guess, hash, @@ -647,9 +645,7 @@ impl<'a, 'tcx> Decodable> for ExpnId { debug_assert_eq!(expn_id.krate, krate); expn_id } -} -impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { fn decode_span(&mut self) -> Span { let ctxt = SyntaxContext::decode(self); let parent = Option::::decode(self); @@ -686,72 +682,62 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { Span::new(lo, hi, ctxt, parent) } -} -// copy&paste impl from rustc_metadata -impl<'a, 'tcx> Decodable> for Symbol { + // copy&paste impl from rustc_metadata #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - let tag = d.read_u8(); + fn decode_symbol(&mut self) -> Symbol { + let tag = self.read_u8(); match tag { SYMBOL_STR => { - let s = d.read_str(); + let s = self.read_str(); Symbol::intern(s) } SYMBOL_OFFSET => { // read str offset - let pos = d.read_usize(); + let pos = self.read_usize(); // move to str offset and read - d.opaque.with_position(pos, |d| { + self.opaque.with_position(pos, |d| { let s = d.read_str(); Symbol::intern(s) }) } SYMBOL_PREINTERNED => { - let symbol_index = d.read_u32(); + let symbol_index = self.read_u32(); Symbol::new_from_decoded(symbol_index) } _ => unreachable!(), } } -} -impl<'a, 'tcx> Decodable> for CrateNum { - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - let stable_id = StableCrateId::decode(d); - let cnum = d.tcx.stable_crate_id_to_crate_num(stable_id); + fn decode_crate_num(&mut self) -> CrateNum { + let stable_id = StableCrateId::decode(self); + let cnum = self.tcx.stable_crate_id_to_crate_num(stable_id); cnum } -} -// This impl makes sure that we get a runtime error when we try decode a -// `DefIndex` that is not contained in a `DefId`. Such a case would be problematic -// because we would not know how to transform the `DefIndex` to the current -// context. -impl<'a, 'tcx> Decodable> for DefIndex { - fn decode(_d: &mut CacheDecoder<'a, 'tcx>) -> DefIndex { + // This impl makes sure that we get a runtime error when we try decode a + // `DefIndex` that is not contained in a `DefId`. Such a case would be problematic + // because we would not know how to transform the `DefIndex` to the current + // context. + fn decode_def_index(&mut self) -> DefIndex { panic!("trying to decode `DefIndex` outside the context of a `DefId`") } -} -// Both the `CrateNum` and the `DefIndex` of a `DefId` can change in between two -// compilation sessions. We use the `DefPathHash`, which is stable across -// sessions, to map the old `DefId` to the new one. -impl<'a, 'tcx> Decodable> for DefId { - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + // Both the `CrateNum` and the `DefIndex` of a `DefId` can change in between two + // compilation sessions. We use the `DefPathHash`, which is stable across + // sessions, to map the old `DefId` to the new one. + fn decode_def_id(&mut self) -> DefId { // Load the `DefPathHash` which is was we encoded the `DefId` as. - let def_path_hash = DefPathHash::decode(d); + let def_path_hash = DefPathHash::decode(self); // Using the `DefPathHash`, we can lookup the new `DefId`. // Subtle: We only encode a `DefId` as part of a query result. // If we get to this point, then all of the query inputs were green, // which means that the definition with this hash is guaranteed to // still exist in the current compilation session. - d.tcx.def_path_hash_to_def_id(def_path_hash, &mut || { + self.tcx.def_path_hash_to_def_id(def_path_hash, &mut || { panic!("Failed to convert DefPathHash {def_path_hash:?}") }) } @@ -860,20 +846,16 @@ impl<'a, 'tcx> CacheEncoder<'a, 'tcx> { } } -impl<'a, 'tcx> Encodable> for SyntaxContext { - fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { - rustc_span::hygiene::raw_encode_syntax_context(*self, s.hygiene_context, s); - } -} - -impl<'a, 'tcx> Encodable> for ExpnId { - fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { - s.hygiene_context.schedule_expn_data_for_encoding(*self); - self.expn_hash().encode(s); - } -} - impl<'a, 'tcx> SpanEncoder for CacheEncoder<'a, 'tcx> { + fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { + rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_context, self); + } + + fn encode_expn_id(&mut self, expn_id: ExpnId) { + self.hygiene_context.schedule_expn_data_for_encoding(expn_id); + expn_id.expn_hash().encode(self); + } + fn encode_span(&mut self, span: Span) { let span_data = span.data_untracked(); span_data.ctxt.encode(self); @@ -915,32 +897,42 @@ impl<'a, 'tcx> SpanEncoder for CacheEncoder<'a, 'tcx> { col_lo.encode(self); len.encode(self); } -} -// copy&paste impl from rustc_metadata -impl<'a, 'tcx> Encodable> for Symbol { - fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { + // copy&paste impl from rustc_metadata + fn encode_symbol(&mut self, symbol: Symbol) { // if symbol preinterned, emit tag and symbol index - if self.is_preinterned() { - s.encoder.emit_u8(SYMBOL_PREINTERNED); - s.encoder.emit_u32(self.as_u32()); + if symbol.is_preinterned() { + self.encoder.emit_u8(SYMBOL_PREINTERNED); + self.encoder.emit_u32(symbol.as_u32()); } else { // otherwise write it as string or as offset to it - match s.symbol_table.entry(*self) { + match self.symbol_table.entry(symbol) { Entry::Vacant(o) => { - s.encoder.emit_u8(SYMBOL_STR); - let pos = s.encoder.position(); + self.encoder.emit_u8(SYMBOL_STR); + let pos = self.encoder.position(); o.insert(pos); - s.emit_str(self.as_str()); + self.emit_str(symbol.as_str()); } Entry::Occupied(o) => { let x = *o.get(); - s.emit_u8(SYMBOL_OFFSET); - s.emit_usize(x); + self.emit_u8(SYMBOL_OFFSET); + self.emit_usize(x); } } } } + + fn encode_crate_num(&mut self, crate_num: CrateNum) { + self.tcx.stable_crate_id(crate_num).encode(self); + } + + fn encode_def_id(&mut self, def_id: DefId) { + self.tcx.def_path_hash(def_id).encode(self); + } + + fn encode_def_index(&mut self, _def_index: DefIndex) { + bug!("encoding `DefIndex` without context"); + } } impl<'a, 'tcx> TyEncoder for CacheEncoder<'a, 'tcx> { @@ -967,26 +959,6 @@ impl<'a, 'tcx> TyEncoder for CacheEncoder<'a, 'tcx> { } } -impl<'a, 'tcx> Encodable> for CrateNum { - #[inline] - fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { - s.tcx.stable_crate_id(*self).encode(s); - } -} - -impl<'a, 'tcx> Encodable> for DefId { - #[inline] - fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { - s.tcx.def_path_hash(*self).encode(s); - } -} - -impl<'a, 'tcx> Encodable> for DefIndex { - fn encode(&self, _: &mut CacheEncoder<'a, 'tcx>) { - bug!("encoding `DefIndex` without context"); - } -} - macro_rules! encoder_methods { ($($name:ident($ty:ty);)*) => { #[inline] diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index e397fab54593..b498202dc921 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -1,11 +1,11 @@ -use crate::{HashStableContext, Symbol}; +use crate::{HashStableContext, SpanDecoder, SpanEncoder, Symbol}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher, ToStableHashKey}; use rustc_data_structures::unhash::Unhasher; use rustc_data_structures::AtomicRef; use rustc_index::Idx; use rustc_macros::HashStable_Generic; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use rustc_serialize::{Decodable, Encodable}; use std::fmt; use std::hash::{BuildHasherDefault, Hash, Hasher}; @@ -46,20 +46,6 @@ impl fmt::Display for CrateNum { } } -/// As a local identifier, a `CrateNum` is only meaningful within its context, e.g. within a tcx. -/// Therefore, make sure to include the context when encode a `CrateNum`. -impl Encodable for CrateNum { - default fn encode(&self, s: &mut E) { - s.emit_u32(self.as_u32()); - } -} - -impl Decodable for CrateNum { - default fn decode(d: &mut D) -> CrateNum { - CrateNum::from_u32(d.read_u32()) - } -} - /// A `DefPathHash` is a fixed-size representation of a `DefPath` that is /// stable across crate and compilation session boundaries. It consists of two /// separate 64-bit hashes. The first uniquely identifies the crate this @@ -220,18 +206,6 @@ rustc_index::newtype_index! { } } -impl Encodable for DefIndex { - default fn encode(&self, _: &mut E) { - panic!("cannot encode `DefIndex` with `{}`", std::any::type_name::()); - } -} - -impl Decodable for DefIndex { - default fn decode(_: &mut D) -> DefIndex { - panic!("cannot decode `DefIndex` with `{}`", std::any::type_name::()); - } -} - /// A `DefId` identifies a particular *definition*, by combining a crate /// index and a def index. /// @@ -347,19 +321,6 @@ impl From for DefId { } } -impl Encodable for DefId { - default fn encode(&self, s: &mut E) { - self.krate.encode(s); - self.index.encode(s); - } -} - -impl Decodable for DefId { - default fn decode(d: &mut D) -> DefId { - DefId { krate: Decodable::decode(d), index: Decodable::decode(d) } - } -} - pub fn default_def_id_debug(def_id: DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DefId").field("krate", &def_id.krate).field("index", &def_id.index).finish() } @@ -423,13 +384,13 @@ impl fmt::Debug for LocalDefId { } } -impl Encodable for LocalDefId { +impl Encodable for LocalDefId { fn encode(&self, s: &mut E) { self.to_def_id().encode(s); } } -impl Decodable for LocalDefId { +impl Decodable for LocalDefId { fn decode(d: &mut D) -> LocalDefId { DefId::decode(d).expect_local() } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index b717229b68df..c4557017c87e 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -27,7 +27,7 @@ use crate::def_id::{CrateNum, DefId, StableCrateId, CRATE_DEF_ID, LOCAL_CRATE}; use crate::edition::Edition; use crate::symbol::{kw, sym, Symbol}; -use crate::{with_session_globals, HashStableContext, Span, DUMMY_SP}; +use crate::{with_session_globals, HashStableContext, Span, SpanDecoder, SpanEncoder, DUMMY_SP}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{Hash64, HashStable, HashingControls, StableHasher}; @@ -1431,30 +1431,18 @@ fn for_all_expns_in( } } -impl Encodable for LocalExpnId { +impl Encodable for LocalExpnId { fn encode(&self, e: &mut E) { self.to_expn_id().encode(e); } } -impl Encodable for ExpnId { - default fn encode(&self, _: &mut E) { - panic!("cannot encode `ExpnId` with `{}`", std::any::type_name::()); - } -} - -impl Decodable for LocalExpnId { +impl Decodable for LocalExpnId { fn decode(d: &mut D) -> Self { ExpnId::expect_local(ExpnId::decode(d)) } } -impl Decodable for ExpnId { - default fn decode(_: &mut D) -> Self { - panic!("cannot decode `ExpnId` with `{}`", std::any::type_name::()); - } -} - pub fn raw_encode_syntax_context( ctxt: SyntaxContext, context: &HygieneEncodeContext, @@ -1466,18 +1454,6 @@ pub fn raw_encode_syntax_context( ctxt.0.encode(e); } -impl Encodable for SyntaxContext { - default fn encode(&self, _: &mut E) { - panic!("cannot encode `SyntaxContext` with `{}`", std::any::type_name::()); - } -} - -impl Decodable for SyntaxContext { - default fn decode(_: &mut D) -> Self { - panic!("cannot decode `SyntaxContext` with `{}`", std::any::type_name::()); - } -} - /// Updates the `disambiguator` field of the corresponding `ExpnData` /// such that the `Fingerprint` of the `ExpnData` does not collide with /// any other `ExpnIds`. diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 0eafbae6e82b..18d1f14cf44d 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -61,7 +61,7 @@ pub use hygiene::{DesugaringKind, ExpnKind, MacroKind}; pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext}; use rustc_data_structures::stable_hasher::HashingControls; pub mod def_id; -use def_id::{CrateNum, DefId, DefPathHash, LocalDefId, StableCrateId, LOCAL_CRATE}; +use def_id::{CrateNum, DefId, DefIndex, DefPathHash, LocalDefId, StableCrateId, LOCAL_CRATE}; pub mod edit_distance; mod span_encoding; pub use span_encoding::{Span, DUMMY_SP}; @@ -1021,6 +1021,14 @@ impl Default for Span { pub trait SpanEncoder: Encoder { fn encode_span(&mut self, span: Span); + fn encode_symbol(&mut self, symbol: Symbol); + fn encode_expn_id(&mut self, expn_id: ExpnId); + fn encode_syntax_context(&mut self, syntax_context: SyntaxContext); + /// As a local identifier, a `CrateNum` is only meaningful within its context, e.g. within a tcx. + /// Therefore, make sure to include the context when encode a `CrateNum`. + fn encode_crate_num(&mut self, crate_num: CrateNum); + fn encode_def_index(&mut self, def_index: DefIndex); + fn encode_def_id(&mut self, def_id: DefId); } impl SpanEncoder for FileEncoder { @@ -1029,6 +1037,31 @@ impl SpanEncoder for FileEncoder { span.lo.encode(self); span.hi.encode(self); } + + fn encode_symbol(&mut self, symbol: Symbol) { + self.emit_str(symbol.as_str()); + } + + fn encode_expn_id(&mut self, _expn_id: ExpnId) { + panic!("cannot encode `ExpnId` with `FileEncoder`"); + } + + fn encode_syntax_context(&mut self, _syntax_context: SyntaxContext) { + panic!("cannot encode `SyntaxContext` with `FileEncoder`"); + } + + fn encode_crate_num(&mut self, crate_num: CrateNum) { + self.emit_u32(crate_num.as_u32()); + } + + fn encode_def_index(&mut self, _def_index: DefIndex) { + panic!("cannot encode `DefIndex` with `FileEncoder`"); + } + + fn encode_def_id(&mut self, def_id: DefId) { + def_id.krate.encode(self); + def_id.index.encode(self); + } } impl Encodable for Span { @@ -1037,8 +1070,50 @@ impl Encodable for Span { } } +impl Encodable for Symbol { + fn encode(&self, s: &mut E) { + s.encode_symbol(*self); + } +} + +impl Encodable for ExpnId { + fn encode(&self, s: &mut E) { + s.encode_expn_id(*self) + } +} + +impl Encodable for SyntaxContext { + fn encode(&self, s: &mut E) { + s.encode_syntax_context(*self) + } +} + +impl Encodable for CrateNum { + fn encode(&self, s: &mut E) { + s.encode_crate_num(*self) + } +} + +impl Encodable for DefIndex { + fn encode(&self, s: &mut E) { + s.encode_def_index(*self) + } +} + +impl Encodable for DefId { + fn encode(&self, s: &mut E) { + s.encode_def_id(*self) + } +} + pub trait SpanDecoder: Decoder { fn decode_span(&mut self) -> Span; + fn decode_symbol(&mut self) -> Symbol; + fn decode_expn_id(&mut self) -> ExpnId; + fn decode_syntax_context(&mut self) -> SyntaxContext; + fn decode_crate_num(&mut self) -> CrateNum; + fn decode_def_index(&mut self) -> DefIndex; + fn decode_def_id(&mut self) -> DefId; } impl SpanDecoder for MemDecoder<'_> { @@ -1048,6 +1123,30 @@ impl SpanDecoder for MemDecoder<'_> { Span::new(lo, hi, SyntaxContext::root(), None) } + + fn decode_symbol(&mut self) -> Symbol { + Symbol::intern(self.read_str()) + } + + fn decode_expn_id(&mut self) -> ExpnId { + panic!("cannot decode `ExpnId` with `MemDecoder`"); + } + + fn decode_syntax_context(&mut self) -> SyntaxContext { + panic!("cannot decode `SyntaxContext` with `MemDecoder`"); + } + + fn decode_crate_num(&mut self) -> CrateNum { + CrateNum::from_u32(self.read_u32()) + } + + fn decode_def_index(&mut self) -> DefIndex { + panic!("cannot decode `DefIndex` with `MemDecoder`"); + } + + fn decode_def_id(&mut self) -> DefId { + DefId { krate: Decodable::decode(self), index: Decodable::decode(self) } + } } impl Decodable for Span { @@ -1056,6 +1155,42 @@ impl Decodable for Span { } } +impl Decodable for Symbol { + fn decode(s: &mut D) -> Symbol { + s.decode_symbol() + } +} + +impl Decodable for ExpnId { + fn decode(s: &mut D) -> ExpnId { + s.decode_expn_id() + } +} + +impl Decodable for SyntaxContext { + fn decode(s: &mut D) -> SyntaxContext { + s.decode_syntax_context() + } +} + +impl Decodable for CrateNum { + fn decode(s: &mut D) -> CrateNum { + s.decode_crate_num() + } +} + +impl Decodable for DefIndex { + fn decode(s: &mut D) -> DefIndex { + s.decode_def_index() + } +} + +impl Decodable for DefId { + fn decode(s: &mut D) -> DefId { + s.decode_def_id() + } +} + /// Insert `source_map` into the session globals for the duration of the /// closure's execution. pub fn set_source_map T>(source_map: Lrc, f: F) -> T { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0b44071496ea..6e59806cf076 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -7,7 +7,6 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_data_structures::sync::Lock; use rustc_macros::HashStable_Generic; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::fmt; use std::hash::{Hash, Hasher}; @@ -2075,19 +2074,6 @@ impl ToString for Symbol { } } -impl Encodable for Symbol { - default fn encode(&self, s: &mut S) { - s.emit_str(self.as_str()); - } -} - -impl Decodable for Symbol { - #[inline] - default fn decode(d: &mut D) -> Symbol { - Symbol::intern(d.read_str()) - } -} - impl HashStable for Symbol { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { From 47936b4813398435189501f2390f27296187c32c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:40:09 +0000 Subject: [PATCH 025/257] Avoid specialization for AttrId deserialization --- compiler/rustc_ast/src/ast.rs | 18 +-------------- compiler/rustc_metadata/src/rmeta/decoder.rs | 9 +++----- .../rustc_middle/src/query/on_disk_cache.rs | 4 ++++ compiler/rustc_span/src/lib.rs | 22 +++++++++++++++++++ 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 1812cc335a4f..e1e4e5fc5675 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -20,6 +20,7 @@ pub use crate::format::*; pub use crate::util::parser::ExprPrecedence; +pub use rustc_span::AttrId; pub use GenericArgs::*; pub use UnsafeSource::*; @@ -30,7 +31,6 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; use rustc_macros::HashStable_Generic; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; @@ -2682,22 +2682,6 @@ pub enum AttrStyle { Inner, } -rustc_index::newtype_index! { - #[orderable] - #[debug_format = "AttrId({})"] - pub struct AttrId {} -} - -impl Encodable for AttrId { - fn encode(&self, _s: &mut S) {} -} - -impl Decodable for AttrId { - default fn decode(_: &mut D) -> AttrId { - panic!("cannot decode `AttrId` with `{}`", std::any::type_name::()); - } -} - /// A list of attributes. pub type AttrVec = ThinVec; diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index d20f4bc7a941..ebba8ee683d4 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -416,15 +416,12 @@ impl<'a, 'tcx> Decodable> for ExpnIndex { } } -impl<'a, 'tcx> Decodable> for ast::AttrId { - #[inline] - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> ast::AttrId { - let sess = d.sess.expect("can't decode AttrId without Session"); +impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> { + fn decode_attr_id(&mut self) -> rustc_span::AttrId { + let sess = self.sess.expect("can't decode AttrId without Session"); sess.parse_sess.attr_id_generator.mk_attr_id() } -} -impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> { fn decode_crate_num(&mut self) -> CrateNum { let cnum = CrateNum::from_u32(self.read_u32()); self.map_encoded_cnum_to_current(cnum) diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 8d084f5f1311..9ca830af9cdf 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -741,6 +741,10 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { panic!("Failed to convert DefPathHash {def_path_hash:?}") }) } + + fn decode_attr_id(&mut self) -> rustc_span::AttrId { + panic!("cannot decode `AttrId` with `CacheDecoder`"); + } } impl<'a, 'tcx> Decodable> for &'tcx UnordSet { diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 18d1f14cf44d..9bd3cabcc854 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1019,6 +1019,12 @@ impl Default for Span { } } +rustc_index::newtype_index! { + #[orderable] + #[debug_format = "AttrId({})"] + pub struct AttrId {} +} + pub trait SpanEncoder: Encoder { fn encode_span(&mut self, span: Span); fn encode_symbol(&mut self, symbol: Symbol); @@ -1106,6 +1112,11 @@ impl Encodable for DefId { } } +impl Encodable for AttrId { + fn encode(&self, _s: &mut E) { + // A fresh id will be generated when decoding + } +} pub trait SpanDecoder: Decoder { fn decode_span(&mut self) -> Span; fn decode_symbol(&mut self) -> Symbol; @@ -1114,6 +1125,7 @@ pub trait SpanDecoder: Decoder { fn decode_crate_num(&mut self) -> CrateNum; fn decode_def_index(&mut self) -> DefIndex; fn decode_def_id(&mut self) -> DefId; + fn decode_attr_id(&mut self) -> AttrId; } impl SpanDecoder for MemDecoder<'_> { @@ -1147,6 +1159,10 @@ impl SpanDecoder for MemDecoder<'_> { fn decode_def_id(&mut self) -> DefId { DefId { krate: Decodable::decode(self), index: Decodable::decode(self) } } + + fn decode_attr_id(&mut self) -> AttrId { + panic!("cannot decode `AttrId` with `MemDecoder`"); + } } impl Decodable for Span { @@ -1191,6 +1207,12 @@ impl Decodable for DefId { } } +impl Decodable for AttrId { + fn decode(s: &mut D) -> AttrId { + s.decode_attr_id() + } +} + /// Insert `source_map` into the session globals for the duration of the /// closure's execution. pub fn set_source_map T>(source_map: Lrc, f: F) -> T { From 3e69335c447a3ca93e4957ee2f4e66d13149e38c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:58:36 +0000 Subject: [PATCH 026/257] Fix tidy error --- compiler/rustc_type_ir/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 3a94256f402f..59966423f7fb 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -10,8 +10,8 @@ derivative = "2.2.0" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } -rustc_span = { path = "../rustc_span", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } +rustc_span = { path = "../rustc_span", optional = true } smallvec = { version = "1.8.1" } # tidy-alphabetical-end @@ -21,8 +21,8 @@ nightly = [ "smallvec/may_dangle", "smallvec/union", "rustc_index/nightly", - "rustc_span", "rustc_serialize", + "rustc_span", "rustc_data_structures", "rustc_macros", ] From 613774e33123c4b3acc397c697def7a55e405653 Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Mon, 1 Jan 2024 11:17:06 +0800 Subject: [PATCH 027/257] feat: add quickfix for redundant_assoc_item diagnostic --- .../trait_impl_redundant_assoc_item.rs | 117 ++++++++++++++---- 1 file changed, 91 insertions(+), 26 deletions(-) diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index c202264bb566..9992a0f82c9e 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -1,5 +1,12 @@ -use hir::{Const, Function, HasSource, TypeAlias}; -use ide_db::base_db::FileRange; +use hir::{db::ExpandDatabase, Const, Function, HasSource, HirDisplay, TypeAlias}; +use ide_db::{ + assists::{Assist, AssistId, AssistKind}, + base_db::FileRange, + label::Label, + source_change::SourceChangeBuilder, +}; +use syntax::{AstNode, SyntaxKind}; +use text_edit::TextRange; use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; @@ -10,35 +17,48 @@ pub(crate) fn trait_impl_redundant_assoc_item( ctx: &DiagnosticsContext<'_>, d: &hir::TraitImplRedundantAssocItems, ) -> Diagnostic { - let name = d.assoc_item.0.clone(); - let assoc_item = d.assoc_item.1; let db = ctx.sema.db; + let name = d.assoc_item.0.clone(); + let redundant_assoc_item_name = name.display(db); + let assoc_item = d.assoc_item.1; let default_range = d.impl_.syntax_node_ptr().text_range(); let trait_name = d.trait_.name(db).to_smol_str(); - let (redundant_item_name, diagnostic_range) = match assoc_item { - hir::AssocItem::Function(id) => ( - format!("`fn {}`", name.display(db)), - Function::from(id) - .source(db) - .map(|it| it.syntax().value.text_range()) - .unwrap_or(default_range), - ), - hir::AssocItem::Const(id) => ( - format!("`const {}`", name.display(db)), - Const::from(id) - .source(db) - .map(|it| it.syntax().value.text_range()) - .unwrap_or(default_range), - ), - hir::AssocItem::TypeAlias(id) => ( - format!("`type {}`", name.display(db)), - TypeAlias::from(id) - .source(db) - .map(|it| it.syntax().value.text_range()) - .unwrap_or(default_range), - ), + let (redundant_item_name, diagnostic_range, redundant_item_def) = match assoc_item { + hir::AssocItem::Function(id) => { + let function = Function::from(id); + ( + format!("`fn {}`", redundant_assoc_item_name), + function + .source(db) + .map(|it| it.syntax().value.text_range()) + .unwrap_or(default_range), + format!("\n {};", function.display(db).to_string()), + ) + } + hir::AssocItem::Const(id) => { + let constant = Const::from(id); + ( + format!("`const {}`", redundant_assoc_item_name), + constant + .source(db) + .map(|it| it.syntax().value.text_range()) + .unwrap_or(default_range), + format!("\n {};", constant.display(db).to_string()), + ) + } + hir::AssocItem::TypeAlias(id) => { + let type_alias = TypeAlias::from(id); + ( + format!("`type {}`", redundant_assoc_item_name), + type_alias + .source(db) + .map(|it| it.syntax().value.text_range()) + .unwrap_or(default_range), + format!("\n type {};", type_alias.name(ctx.sema.db).to_smol_str()), + ) + } }; Diagnostic::new( @@ -46,6 +66,51 @@ pub(crate) fn trait_impl_redundant_assoc_item( format!("{redundant_item_name} is not a member of trait `{trait_name}`"), FileRange { file_id: d.file_id.file_id().unwrap(), range: diagnostic_range }, ) + .with_fixes(quickfix_for_redundant_assoc_item( + ctx, + d, + redundant_item_def, + diagnostic_range, + )) +} + +/// add assoc item into the trait def body +fn quickfix_for_redundant_assoc_item( + ctx: &DiagnosticsContext<'_>, + d: &hir::TraitImplRedundantAssocItems, + redundant_item_def: String, + range: TextRange, +) -> Option> { + let add_assoc_item_def = |builder: &mut SourceChangeBuilder| -> Option<()> { + let db = ctx.sema.db; + let root = db.parse_or_expand(d.file_id); + // don't modify trait def in outer crate + let current_crate = ctx.sema.scope(&d.impl_.syntax_node_ptr().to_node(&root))?.krate(); + let trait_def_crate = d.trait_.module(db).krate(); + if trait_def_crate != current_crate { + return None; + } + let trait_def = d.trait_.source(db)?.value; + let where_to_insert = trait_def + .syntax() + .descendants_with_tokens() + .find(|it| it.kind() == SyntaxKind::L_CURLY) + .map(|it| it.text_range())?; + + Some(builder.insert(where_to_insert.end(), redundant_item_def)) + }; + let file_id = d.file_id.file_id()?; + let mut source_change_builder = SourceChangeBuilder::new(file_id); + add_assoc_item_def(&mut source_change_builder)?; + + Some(vec![Assist { + id: AssistId("add assoc item def into trait def", AssistKind::QuickFix), + label: Label::new("Add assoc item def into trait def".to_string()), + group: None, + target: range, + source_change: Some(source_change_builder.finish()), + trigger_signature_help: false, + }]) } #[cfg(test)] From 4eb3d2e279a6466c78fdfb19710898dc04e9a0f0 Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Mon, 1 Jan 2024 11:19:12 +0800 Subject: [PATCH 028/257] test: add test case for redundant_assoc_item quickfix --- .../trait_impl_redundant_assoc_item.rs | 97 ++++++++++++++++++- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index 9992a0f82c9e..be2a7b9ad2ec 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -115,7 +115,98 @@ fn quickfix_for_redundant_assoc_item( #[cfg(test)] mod tests { - use crate::tests::check_diagnostics; + use crate::tests::{check_diagnostics, check_fix, check_no_fix}; + + #[test] + fn quickfix_for_assoc_func() { + check_fix( + r#" +trait Marker { + fn boo(); +} +struct Foo; +impl Marker for Foo { + fn$0 bar(_a: i32, _b: String) -> String {} + fn boo() {} +} + "#, + r#" +trait Marker { + fn bar(_a: i32, _b: String) -> String; + fn boo(); +} +struct Foo; +impl Marker for Foo { + fn bar(_a: i32, _b: String) -> String {} + fn boo() {} +} + "#, + ) + } + + #[test] + fn quickfix_for_assoc_const() { + check_fix( + r#" +trait Marker { + fn foo () {} +} +struct Foo; +impl Marker for Foo { + const FLAG: bool$0 = false; +} + "#, + r#" +trait Marker { + const FLAG: bool; + fn foo () {} +} +struct Foo; +impl Marker for Foo { + const FLAG: bool = false; +} + "#, + ) + } + + #[test] + fn quickfix_for_assoc_type() { + check_fix( + r#" +trait Marker { +} +struct Foo; +impl Marker for Foo { + type T = i32;$0 +} + "#, + r#" +trait Marker { + type T; +} +struct Foo; +impl Marker for Foo { + type T = i32; +} + "#, + ) + } + + #[test] + fn quickfix_dont_work() { + check_no_fix( + r#" + //- /dep.rs crate:dep + trait Marker { + } + //- /main.rs crate:main deps:dep + struct Foo; + impl dep::Marker for Foo { + type T = i32;$0 + } + "#, + ) + } #[test] fn trait_with_default_value() { @@ -129,12 +220,12 @@ trait Marker { struct Foo; impl Marker for Foo { type T = i32; - //^^^^^^^^^^^^^ error: `type T` is not a member of trait `Marker` + //^^^^^^^^^^^^^ 💡 error: `type T` is not a member of trait `Marker` const FLAG: bool = true; fn bar() {} - //^^^^^^^^^^^ error: `fn bar` is not a member of trait `Marker` + //^^^^^^^^^^^ 💡 error: `fn bar` is not a member of trait `Marker` fn boo() {} } From 96bd9cdcc4b70262c1d125794e7ab751f370d587 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 1 Jan 2024 09:41:39 +0300 Subject: [PATCH 029/257] pass allow-{dirty,staged} to clippy Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/check.rs | 13 ++++++++++++- src/bootstrap/src/core/config/flags.rs | 4 ++++ src/etc/completions/x.py.fish | 2 ++ src/etc/completions/x.py.ps1 | 2 ++ src/etc/completions/x.py.sh | 2 +- src/etc/completions/x.py.zsh | 2 ++ 6 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index ecaaf91aec12..97a2494370cc 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -28,7 +28,9 @@ fn args(builder: &Builder<'_>) -> Vec { arr.iter().copied().map(String::from) } - if let Subcommand::Clippy { fix, allow, deny, warn, forbid, .. } = &builder.config.cmd { + if let Subcommand::Clippy { fix, allow_dirty, allow_staged, allow, deny, warn, forbid } = + &builder.config.cmd + { // disable the most spammy clippy lints let ignored_lints = vec![ "many_single_char_names", // there are a lot in stdarch @@ -49,7 +51,16 @@ fn args(builder: &Builder<'_>) -> Vec { // As a workaround, avoid checking tests and benches when passed --fix. "--lib", "--bins", "--examples", ])); + + if *allow_dirty { + args.push("--allow-dirty".to_owned()); + } + + if *allow_staged { + args.push("--allow-staged".to_owned()); + } } + args.extend(strings(&["--", "--cap-lints", "warn"])); args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint))); let mut clippy_lint_levels: Vec = Vec::new(); diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 0b13726081b1..8af454001a64 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -255,6 +255,10 @@ pub enum Subcommand { Clippy { #[arg(long)] fix: bool, + #[arg(long, requires = "fix")] + allow_dirty: bool, + #[arg(long, requires = "fix")] + allow_staged: bool, /// clippy lints to allow #[arg(global(true), short = 'A', action = clap::ArgAction::Append, value_name = "LINT")] allow: Vec, diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 95bce9e31a4d..9fc95fd09ba9 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -143,6 +143,8 @@ complete -c x.py -n "__fish_seen_subcommand_from clippy" -l llvm-profile-use -d complete -c x.py -n "__fish_seen_subcommand_from clippy" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r complete -c x.py -n "__fish_seen_subcommand_from clippy" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from clippy" -l fix +complete -c x.py -n "__fish_seen_subcommand_from clippy" -l allow-dirty +complete -c x.py -n "__fish_seen_subcommand_from clippy" -l allow-staged complete -c x.py -n "__fish_seen_subcommand_from clippy" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from clippy" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_seen_subcommand_from clippy" -l include-default-paths -d 'include default paths in addition to the provided ones' diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index dabc3b16e4d7..6359b7ff086a 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -188,6 +188,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--reproducible-artifact', 'reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('--fix', 'fix', [CompletionResultType]::ParameterName, 'fix') + [CompletionResult]::new('--allow-dirty', 'allow-dirty', [CompletionResultType]::ParameterName, 'allow-dirty') + [CompletionResult]::new('--allow-staged', 'allow-staged', [CompletionResultType]::ParameterName, 'allow-staged') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index f739f46b88bb..8ffd8f67df25 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -615,7 +615,7 @@ _x.py() { return 0 ;; x.py__clippy) - opts="-A -D -W -F -v -i -j -h --fix --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + opts="-A -D -W -F -v -i -j -h --fix --allow-dirty --allow-staged --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index 639f0487f8b0..ea7e4ba6758f 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -182,6 +182,8 @@ _arguments "${_arguments_options[@]}" \ '*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \ '*--set=[override options in config.toml]:section.option=value:( )' \ '--fix[]' \ +'--allow-dirty[]' \ +'--allow-staged[]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \ From 3d9eebc21f4ec00e75fc5dd23f66835634b2e77b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 1 Jan 2024 15:13:39 +0000 Subject: [PATCH 030/257] Fix tests --- tests/ui-fulldeps/deriving-global.rs | 1 + tests/ui-fulldeps/deriving-hygiene.rs | 1 + tests/ui-fulldeps/empty-struct-braces-derive.rs | 1 + tests/ui-fulldeps/rustc_encodable_hygiene.rs | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/ui-fulldeps/deriving-global.rs b/tests/ui-fulldeps/deriving-global.rs index 214bb4368ffd..9c0fc13a5e2f 100644 --- a/tests/ui-fulldeps/deriving-global.rs +++ b/tests/ui-fulldeps/deriving-global.rs @@ -4,6 +4,7 @@ extern crate rustc_macros; extern crate rustc_serialize; +extern crate rustc_span; // Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta // files. diff --git a/tests/ui-fulldeps/deriving-hygiene.rs b/tests/ui-fulldeps/deriving-hygiene.rs index e1084a08fec9..48d3355b9d54 100644 --- a/tests/ui-fulldeps/deriving-hygiene.rs +++ b/tests/ui-fulldeps/deriving-hygiene.rs @@ -4,6 +4,7 @@ #![feature(rustc_private)] extern crate rustc_macros; extern crate rustc_serialize; +extern crate rustc_span; use rustc_macros::{Decodable, Encodable}; diff --git a/tests/ui-fulldeps/empty-struct-braces-derive.rs b/tests/ui-fulldeps/empty-struct-braces-derive.rs index 10e8beaa7b11..3637610af0d9 100644 --- a/tests/ui-fulldeps/empty-struct-braces-derive.rs +++ b/tests/ui-fulldeps/empty-struct-braces-derive.rs @@ -5,6 +5,7 @@ extern crate rustc_macros; extern crate rustc_serialize; +extern crate rustc_span; // Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta // files. diff --git a/tests/ui-fulldeps/rustc_encodable_hygiene.rs b/tests/ui-fulldeps/rustc_encodable_hygiene.rs index 509a6b1d22ca..bec7930d4622 100644 --- a/tests/ui-fulldeps/rustc_encodable_hygiene.rs +++ b/tests/ui-fulldeps/rustc_encodable_hygiene.rs @@ -3,8 +3,8 @@ #![feature(rustc_private)] extern crate rustc_macros; -#[allow(dead_code)] extern crate rustc_serialize; +extern crate rustc_span; // Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta // files. From 8fb8e6eefb27313523be937ff9de557dd3bcf4fe Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 1 Jan 2024 19:10:42 +0000 Subject: [PATCH 031/257] Add comments sugested by reviewer --- compiler/rustc_span/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 9bd3cabcc854..7f7f05b79789 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1025,6 +1025,8 @@ rustc_index::newtype_index! { pub struct AttrId {} } +/// This trait is used to allow encoder specific encodings of certain types. +/// It is similar to rustc_type_ir's TyEncoder. pub trait SpanEncoder: Encoder { fn encode_span(&mut self, span: Span); fn encode_symbol(&mut self, symbol: Symbol); @@ -1117,6 +1119,9 @@ impl Encodable for AttrId { // A fresh id will be generated when decoding } } + +/// This trait is used to allow decoder specific encodings of certain types. +/// It is similar to rustc_type_ir's TyDecoder. pub trait SpanDecoder: Decoder { fn decode_span(&mut self) -> Span; fn decode_symbol(&mut self) -> Symbol; From af7819477a087baf84f592456660bbc5666fa909 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 24 Sep 2023 08:11:11 +0000 Subject: [PATCH 032/257] Reuse eligible_storage_live memory. --- compiler/rustc_mir_transform/src/coroutine.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index ce1a36cf6702..193479cc59f3 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -942,6 +942,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( body, saved_locals: saved_locals, local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()), + eligible_storage_live: BitSet::new_empty(body.local_decls.len()), }; requires_storage.visit_reachable_with(body, &mut visitor); @@ -978,6 +979,8 @@ struct StorageConflictVisitor<'mir, 'tcx, 's> { // FIXME(tmandry): Consider using sparse bitsets here once we have good // benchmarks for coroutines. local_conflicts: BitMatrix, + // We keep this bitset as a buffer to avoid reallocating memory. + eligible_storage_live: BitSet, } impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> @@ -1009,19 +1012,19 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> impl StorageConflictVisitor<'_, '_, '_> { fn apply_state(&mut self, flow_state: &BitSet, loc: Location) { // Ignore unreachable blocks. - if self.body.basic_blocks[loc.block].terminator().kind == TerminatorKind::Unreachable { + if let TerminatorKind::Unreachable = self.body.basic_blocks[loc.block].terminator().kind { return; } - let mut eligible_storage_live = flow_state.clone(); - eligible_storage_live.intersect(&**self.saved_locals); + self.eligible_storage_live.clone_from(flow_state); + self.eligible_storage_live.intersect(&**self.saved_locals); - for local in eligible_storage_live.iter() { - self.local_conflicts.union_row_with(&eligible_storage_live, local); + for local in self.eligible_storage_live.iter() { + self.local_conflicts.union_row_with(&self.eligible_storage_live, local); } - if eligible_storage_live.count() > 1 { - trace!("at {:?}, eligible_storage_live={:?}", loc, eligible_storage_live); + if self.eligible_storage_live.count() > 1 { + trace!("at {:?}, eligible_storage_live={:?}", loc, self.eligible_storage_live); } } } From 0adfe207d7e134fcc130d7bd57a3c6e96f63f8da Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 24 Sep 2023 09:05:18 +0000 Subject: [PATCH 033/257] Reuse `bitwise` in BitMatrix. --- compiler/rustc_index/src/bit_set.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index d730ef58deba..892c408963ab 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1699,14 +1699,15 @@ impl BitMatrix { let (read_start, read_end) = self.range(read); let (write_start, write_end) = self.range(write); let words = &mut self.words[..]; - let mut changed = false; + let mut changed = 0; for (read_index, write_index) in iter::zip(read_start..read_end, write_start..write_end) { let word = words[write_index]; let new_word = word | words[read_index]; words[write_index] = new_word; - changed |= word != new_word; + // See `bitwise` for the rationale. + changed |= word ^ new_word; } - changed + changed != 0 } /// Adds the bits from `with` to the bits from row `write`, and @@ -1715,14 +1716,7 @@ impl BitMatrix { assert!(write.index() < self.num_rows); assert_eq!(with.domain_size(), self.num_columns); let (write_start, write_end) = self.range(write); - let mut changed = false; - for (read_index, write_index) in iter::zip(0..with.words.len(), write_start..write_end) { - let word = self.words[write_index]; - let new_word = word | with.words[read_index]; - self.words[write_index] = new_word; - changed |= word != new_word; - } - changed + bitwise(&mut self.words[write_start..write_end], &with.words, |a, b| a | b) } /// Sets every cell in `row` to true. From 932d85b52946d917deab2c23ead552f7f713b828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 3 Jan 2024 11:35:07 +0200 Subject: [PATCH 034/257] Merge commit '426d2842c1f0e5cc5e34bb37c7ac3ee0945f9746' into sync-from-ra2 --- .cargo/config.toml | 2 +- .github/workflows/ci.yaml | 1 - Cargo.lock | 87 +- Cargo.toml | 25 +- crates/base-db/Cargo.toml | 7 +- crates/base-db/src/change.rs | 18 +- crates/base-db/src/input.rs | 64 +- crates/base-db/src/lib.rs | 30 +- crates/cfg/Cargo.toml | 5 +- crates/cfg/src/tests.rs | 10 +- crates/flycheck/Cargo.toml | 9 +- crates/hir-def/Cargo.toml | 11 +- crates/hir-def/src/attr.rs | 21 +- crates/hir-def/src/attr/tests.rs | 10 +- crates/hir-def/src/body/lower.rs | 78 +- crates/hir-def/src/body/scope.rs | 3 +- crates/hir-def/src/body/tests.rs | 3 +- crates/hir-def/src/data.rs | 4 +- crates/hir-def/src/db.rs | 110 +- crates/hir-def/src/expander.rs | 72 +- crates/hir-def/src/find_path.rs | 2 +- crates/hir-def/src/import_map.rs | 3 +- crates/hir-def/src/item_scope.rs | 10 +- crates/hir-def/src/item_tree.rs | 19 +- crates/hir-def/src/item_tree/lower.rs | 8 +- crates/hir-def/src/item_tree/pretty.rs | 4 +- crates/hir-def/src/item_tree/tests.rs | 2 +- crates/hir-def/src/lib.rs | 189 +--- crates/hir-def/src/lower.rs | 2 +- .../macro_expansion_tests/builtin_fn_macro.rs | 42 +- .../hir-def/src/macro_expansion_tests/mbe.rs | 14 +- .../macro_expansion_tests/mbe/meta_syntax.rs | 4 +- .../macro_expansion_tests/mbe/metavar_expr.rs | 78 +- .../hir-def/src/macro_expansion_tests/mod.rs | 26 +- crates/hir-def/src/nameres.rs | 14 +- crates/hir-def/src/nameres/attr_resolution.rs | 3 +- crates/hir-def/src/nameres/collector.rs | 116 ++- crates/hir-def/src/nameres/proc_macro.rs | 10 +- crates/hir-def/src/nameres/tests.rs | 3 +- .../hir-def/src/nameres/tests/incremental.rs | 7 +- crates/hir-def/src/nameres/tests/macros.rs | 8 +- crates/hir-def/src/resolver.rs | 14 +- crates/hir-def/src/visibility.rs | 2 +- crates/hir-expand/Cargo.toml | 6 +- crates/hir-expand/src/ast_id_map.rs | 27 +- crates/hir-expand/src/attrs.rs | 22 +- crates/hir-expand/src/builtin_attr_macro.rs | 27 +- crates/hir-expand/src/builtin_derive_macro.rs | 69 +- crates/hir-expand/src/builtin_fn_macro.rs | 168 +-- crates/hir-expand/src/change.rs | 42 + crates/hir-expand/src/db.rs | 191 +++- crates/hir-expand/src/eager.rs | 26 +- crates/hir-expand/src/files.rs | 7 +- crates/hir-expand/src/fixup.rs | 77 +- crates/hir-expand/src/hygiene.rs | 43 +- crates/hir-expand/src/lib.rs | 222 ++-- crates/hir-expand/src/mod_path.rs | 5 +- crates/hir-expand/src/name.rs | 5 + crates/hir-expand/src/proc_macro.rs | 58 +- crates/hir-expand/src/quote.rs | 35 +- crates/hir-expand/src/span.rs | 124 --- crates/hir-expand/src/span_map.rs | 65 ++ crates/hir-ty/Cargo.toml | 8 +- crates/hir-ty/src/consteval/tests.rs | 3 +- crates/hir-ty/src/infer.rs | 10 + crates/hir-ty/src/infer/expr.rs | 19 + crates/hir-ty/src/infer/path.rs | 3 + crates/hir-ty/src/layout/tests.rs | 2 +- crates/hir-ty/src/mir/eval/tests.rs | 3 +- crates/hir-ty/src/tests.rs | 3 +- crates/hir-ty/src/tests/incremental.rs | 3 +- crates/hir-ty/src/tests/patterns.rs | 34 + crates/hir/Cargo.toml | 7 +- crates/hir/src/attrs.rs | 2 +- crates/hir/src/db.rs | 2 +- crates/hir/src/diagnostics.rs | 9 +- crates/hir/src/lib.rs | 33 +- crates/hir/src/semantics.rs | 24 +- crates/hir/src/source_analyzer.rs | 73 +- crates/hir/src/symbols.rs | 16 +- crates/ide-assists/Cargo.toml | 4 + .../ide-assists/src/handlers/auto_import.rs | 7 +- .../ide-assists/src/handlers/bool_to_enum.rs | 227 +++-- .../src/handlers/extract_variable.rs | 229 +++-- .../src/handlers/generate_delegate_trait.rs | 963 +++++++++++++++--- .../src/handlers/generate_enum_variant.rs | 2 +- .../src/handlers/generate_function.rs | 300 +++--- .../ide-assists/src/handlers/inline_call.rs | 134 ++- .../src/handlers/introduce_named_generic.rs | 40 +- .../src/handlers/promote_local_to_const.rs | 89 +- .../src/handlers/remove_unused_imports.rs | 184 +++- .../replace_is_method_with_if_let_method.rs | 34 +- crates/ide-assists/src/tests.rs | 53 +- crates/ide-assists/src/tests/generated.rs | 2 +- crates/ide-assists/src/utils.rs | 18 + crates/ide-assists/src/utils/suggest_name.rs | 56 +- crates/ide-completion/Cargo.toml | 4 + .../src/completions/attribute.rs | 5 + .../src/completions/attribute/macro_use.rs | 35 + crates/ide-completion/src/completions/dot.rs | 31 +- crates/ide-completion/src/context.rs | 3 +- crates/ide-completion/src/context/analysis.rs | 43 +- crates/ide-completion/src/lib.rs | 2 + crates/ide-completion/src/render.rs | 6 +- crates/ide-completion/src/render/function.rs | 15 +- crates/ide-completion/src/tests.rs | 3 +- crates/ide-completion/src/tests/attribute.rs | 79 ++ crates/ide-db/Cargo.toml | 9 +- crates/ide-db/src/apply_change.rs | 8 +- crates/ide-db/src/documentation.rs | 16 +- crates/ide-db/src/imports/insert_use/tests.rs | 2 +- crates/ide-db/src/lib.rs | 2 + crates/ide-db/src/path_transform.rs | 28 + crates/ide-db/src/rename.rs | 7 +- crates/ide-db/src/symbol_index.rs | 8 +- .../test_symbol_index_collection.txt | 142 ++- crates/ide-db/src/traits.rs | 3 +- crates/ide-diagnostics/Cargo.toml | 4 + .../trait_impl_redundant_assoc_item.rs | 20 + .../src/handlers/unresolved_assoc_item.rs | 52 + .../src/handlers/unresolved_method.rs | 204 +++- crates/ide-diagnostics/src/lib.rs | 4 +- crates/ide-diagnostics/src/tests.rs | 5 +- crates/ide-ssr/Cargo.toml | 4 + crates/ide-ssr/src/tests.rs | 4 +- crates/ide/Cargo.toml | 6 +- crates/ide/src/annotations.rs | 331 +++--- crates/ide/src/doc_links.rs | 2 +- crates/ide/src/fixture.rs | 2 +- crates/ide/src/goto_definition.rs | 71 +- crates/ide/src/goto_implementation.rs | 15 +- crates/ide/src/lib.rs | 5 +- crates/ide/src/markdown_remove.rs | 17 +- crates/ide/src/shuffle_crate_graph.rs | 5 +- crates/ide/src/signature_help.rs | 3 +- crates/ide/src/ssr.rs | 5 +- crates/ide/src/status.rs | 55 +- crates/intern/Cargo.toml | 5 +- crates/limit/Cargo.toml | 3 + crates/load-cargo/Cargo.toml | 8 +- crates/load-cargo/src/lib.rs | 50 +- crates/mbe/Cargo.toml | 6 +- crates/mbe/src/benchmark.rs | 20 +- crates/mbe/src/expander.rs | 19 +- crates/mbe/src/expander/matcher.rs | 59 +- crates/mbe/src/expander/transcriber.rs | 110 +- crates/mbe/src/lib.rs | 46 +- crates/mbe/src/parser.rs | 70 +- crates/mbe/src/syntax_bridge.rs | 184 ++-- crates/mbe/src/syntax_bridge/tests.rs | 4 +- crates/mbe/src/tt_iter.rs | 15 +- crates/parser/Cargo.toml | 3 + crates/paths/Cargo.toml | 3 + crates/proc-macro-api/Cargo.toml | 4 + crates/proc-macro-api/src/lib.rs | 35 +- crates/proc-macro-api/src/msg.rs | 69 +- crates/proc-macro-api/src/msg/flat.rs | 61 +- crates/proc-macro-api/src/process.rs | 36 +- crates/proc-macro-srv-cli/Cargo.toml | 3 + crates/proc-macro-srv-cli/src/main.rs | 14 +- crates/proc-macro-srv/Cargo.toml | 9 +- .../proc-macro-srv/proc-macro-test/Cargo.toml | 19 + .../proc-macro-test/build.rs | 3 + .../proc-macro-test/imp/.gitignore | 0 .../proc-macro-test/imp/Cargo.toml | 7 +- .../proc-macro-test/imp/src/lib.rs | 25 + .../proc-macro-test/src/lib.rs | 0 crates/proc-macro-srv/src/dylib.rs | 22 +- crates/proc-macro-srv/src/lib.rs | 194 +++- crates/proc-macro-srv/src/proc_macros.rs | 48 +- crates/proc-macro-srv/src/server.rs | 399 +------- .../src/server/rust_analyzer_span.rs | 411 ++++++++ crates/proc-macro-srv/src/server/token_id.rs | 380 +++++++ .../proc-macro-srv/src/server/token_stream.rs | 111 +- crates/proc-macro-srv/src/tests/mod.rs | 92 +- crates/proc-macro-srv/src/tests/utils.rs | 87 +- crates/proc-macro-test/Cargo.toml | 20 - crates/profile/Cargo.toml | 3 + crates/project-model/Cargo.toml | 7 +- crates/project-model/src/cargo_workspace.rs | 1 + crates/project-model/src/project_json.rs | 3 + crates/project-model/src/workspace.rs | 38 +- .../cargo_hello_world_project_model.txt | 10 +- ...project_model_with_selective_overrides.txt | 10 +- ..._project_model_with_wildcard_overrides.txt | 10 +- ...rust_project_hello_world_project_model.txt | 22 +- crates/rust-analyzer/Cargo.toml | 8 +- crates/rust-analyzer/src/bin/main.rs | 17 +- crates/rust-analyzer/src/caps.rs | 2 + crates/rust-analyzer/src/cargo_target_spec.rs | 4 +- crates/rust-analyzer/src/cli/rustc_tests.rs | 4 +- crates/rust-analyzer/src/cli/scip.rs | 2 +- crates/rust-analyzer/src/config.rs | 46 +- crates/rust-analyzer/src/global_state.rs | 3 +- .../src/handlers/notification.rs | 7 + crates/rust-analyzer/src/handlers/request.rs | 16 +- .../src/integrated_benchmarks.rs | 3 +- crates/rust-analyzer/src/lsp/ext.rs | 2 +- crates/rust-analyzer/src/lsp/to_proto.rs | 2 +- crates/rust-analyzer/src/lsp/utils.rs | 35 +- crates/rust-analyzer/src/reload.rs | 28 +- crates/rust-analyzer/tests/slow-tests/main.rs | 2 +- crates/rustc-dependencies/Cargo.toml | 3 + crates/sourcegen/Cargo.toml | 4 + crates/span/Cargo.toml | 21 + .../{base-db/src/span.rs => span/src/lib.rs} | 88 +- .../{mbe/src/token_map.rs => span/src/map.rs} | 65 +- crates/stdx/Cargo.toml | 3 + crates/syntax/Cargo.toml | 5 +- crates/syntax/fuzz/Cargo.toml | 3 + crates/syntax/src/ast/edit_in_place.rs | 47 +- crates/syntax/src/ast/make.rs | 64 +- crates/syntax/src/ast/node_ext.rs | 44 +- crates/test-fixture/Cargo.toml | 21 + .../fixture.rs => test-fixture/src/lib.rs} | 138 +-- crates/test-utils/Cargo.toml | 5 +- crates/test-utils/src/minicore.rs | 4 + crates/text-edit/Cargo.toml | 3 + crates/toolchain/Cargo.toml | 3 + crates/tt/Cargo.toml | 6 + crates/tt/src/lib.rs | 73 +- crates/vfs-notify/Cargo.toml | 3 + crates/vfs/Cargo.toml | 5 +- docs/dev/architecture.md | 51 +- docs/dev/lsp-extensions.md | 2 +- docs/user/generated_config.adoc | 16 +- editors/code/package-lock.json | 10 +- editors/code/package.json | 26 +- editors/code/src/debug.ts | 6 +- editors/code/src/run.ts | 2 +- lib/la-arena/Cargo.toml | 3 + lib/line-index/Cargo.toml | 3 + lib/lsp-server/Cargo.toml | 9 +- lib/lsp-server/examples/goto_def.rs | 10 +- lib/lsp-server/src/error.rs | 17 +- lib/lsp-server/src/lib.rs | 49 +- lib/lsp-server/src/msg.rs | 4 +- lib/lsp-server/src/stdio.rs | 3 +- xtask/Cargo.toml | 3 + xtask/src/release/changelog.rs | 52 +- 240 files changed, 6941 insertions(+), 3102 deletions(-) create mode 100644 crates/hir-expand/src/change.rs delete mode 100644 crates/hir-expand/src/span.rs create mode 100644 crates/hir-expand/src/span_map.rs create mode 100644 crates/ide-completion/src/completions/attribute/macro_use.rs create mode 100644 crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs create mode 100644 crates/proc-macro-srv/proc-macro-test/Cargo.toml rename crates/{ => proc-macro-srv}/proc-macro-test/build.rs (97%) rename crates/{ => proc-macro-srv}/proc-macro-test/imp/.gitignore (100%) rename crates/{ => proc-macro-srv}/proc-macro-test/imp/Cargo.toml (91%) rename crates/{ => proc-macro-srv}/proc-macro-test/imp/src/lib.rs (80%) rename crates/{ => proc-macro-srv}/proc-macro-test/src/lib.rs (100%) create mode 100644 crates/proc-macro-srv/src/server/rust_analyzer_span.rs create mode 100644 crates/proc-macro-srv/src/server/token_id.rs delete mode 100644 crates/proc-macro-test/Cargo.toml create mode 100644 crates/span/Cargo.toml rename crates/{base-db/src/span.rs => span/src/lib.rs} (72%) rename crates/{mbe/src/token_map.rs => span/src/map.rs} (59%) create mode 100644 crates/test-fixture/Cargo.toml rename crates/{base-db/src/fixture.rs => test-fixture/src/lib.rs} (87%) diff --git a/.cargo/config.toml b/.cargo/config.toml index c9ad7803951a..c3cfda85517e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,7 +2,7 @@ xtask = "run --package xtask --bin xtask --" tq = "test -- -q" qt = "tq" -lint = "clippy --all-targets -- -Aclippy::collapsible_if -Aclippy::needless_pass_by_value -Aclippy::nonminimal_bool -Aclippy::redundant_pattern_matching --cap-lints warn" +lint = "clippy --all-targets -- --cap-lints warn" [target.x86_64-pc-windows-msvc] linker = "rust-lld" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1f2a7796d114..be830415f9cb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -38,7 +38,6 @@ jobs: - 'crates/proc-macro-api/**' - 'crates/proc-macro-srv/**' - 'crates/proc-macro-srv-cli/**' - - 'crates/proc-macro-test/**' rust: needs: changes diff --git a/Cargo.lock b/Cargo.lock index 227d1db0ec72..c7d110eafb66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,11 +74,11 @@ dependencies = [ "profile", "rust-analyzer-salsa", "rustc-hash", + "semver", + "span", "stdx", "syntax", - "test-utils", "triomphe", - "tt", "vfs", ] @@ -516,8 +516,10 @@ dependencies = [ "rustc-dependencies", "rustc-hash", "smallvec", + "span", "stdx", "syntax", + "test-fixture", "test-utils", "tracing", "triomphe", @@ -542,6 +544,7 @@ dependencies = [ "profile", "rustc-hash", "smallvec", + "span", "stdx", "syntax", "tracing", @@ -581,6 +584,7 @@ dependencies = [ "smallvec", "stdx", "syntax", + "test-fixture", "test-utils", "tracing", "tracing-subscriber", @@ -624,6 +628,7 @@ dependencies = [ "smallvec", "stdx", "syntax", + "test-fixture", "test-utils", "text-edit", "toolchain", @@ -647,6 +652,7 @@ dependencies = [ "sourcegen", "stdx", "syntax", + "test-fixture", "test-utils", "text-edit", ] @@ -666,6 +672,7 @@ dependencies = [ "smallvec", "stdx", "syntax", + "test-fixture", "test-utils", "text-edit", ] @@ -694,8 +701,10 @@ dependencies = [ "rayon", "rustc-hash", "sourcegen", + "span", "stdx", "syntax", + "test-fixture", "test-utils", "text-edit", "tracing", @@ -720,6 +729,7 @@ dependencies = [ "sourcegen", "stdx", "syntax", + "test-fixture", "test-utils", "text-edit", ] @@ -737,6 +747,7 @@ dependencies = [ "parser", "stdx", "syntax", + "test-fixture", "test-utils", "text-edit", "triomphe", @@ -903,11 +914,13 @@ version = "0.0.0" dependencies = [ "anyhow", "crossbeam-channel", + "hir-expand", "ide", "ide-db", "itertools", "proc-macro-api", "project-model", + "span", "tracing", "tt", "vfs", @@ -932,19 +945,7 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "lsp-server" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b52dccdf3302eefab8c8a1273047f0a3c3dca4b527c8458d00c09484c8371928" -dependencies = [ - "crossbeam-channel", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "lsp-server" -version = "0.7.5" +version = "0.7.6" dependencies = [ "crossbeam-channel", "ctrlc", @@ -955,10 +956,22 @@ dependencies = [ ] [[package]] -name = "lsp-types" -version = "0.94.0" +name = "lsp-server" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237" +checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" +dependencies = [ + "crossbeam-channel", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "lsp-types" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158c1911354ef73e8fe42da6b10c0484cb65c7f1007f28022e847706c1ab6984" dependencies = [ "bitflags 1.3.2", "serde", @@ -975,6 +988,7 @@ dependencies = [ "parser", "rustc-hash", "smallvec", + "span", "stdx", "syntax", "test-utils", @@ -1251,6 +1265,7 @@ dependencies = [ "serde", "serde_json", "snap", + "span", "stdx", "text-size", "tracing", @@ -1262,6 +1277,7 @@ dependencies = [ name = "proc-macro-srv" version = "0.0.0" dependencies = [ + "base-db", "expect-test", "libloading", "mbe", @@ -1270,6 +1286,7 @@ dependencies = [ "paths", "proc-macro-api", "proc-macro-test", + "span", "stdx", "tt", ] @@ -1287,14 +1304,9 @@ name = "proc-macro-test" version = "0.0.0" dependencies = [ "cargo_metadata", - "proc-macro-test-impl", "toolchain", ] -[[package]] -name = "proc-macro-test-impl" -version = "0.0.0" - [[package]] name = "proc-macro2" version = "1.0.69" @@ -1514,7 +1526,7 @@ dependencies = [ "ide-ssr", "itertools", "load-cargo", - "lsp-server 0.7.4", + "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "mbe", "mimalloc", @@ -1535,6 +1547,7 @@ dependencies = [ "sourcegen", "stdx", "syntax", + "test-fixture", "test-utils", "tikv-jemallocator", "toolchain", @@ -1726,6 +1739,17 @@ dependencies = [ "xshell", ] +[[package]] +name = "span" +version = "0.0.0" +dependencies = [ + "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-analyzer-salsa", + "stdx", + "syntax", + "vfs", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1796,6 +1820,20 @@ dependencies = [ "ungrammar", ] +[[package]] +name = "test-fixture" +version = "0.0.0" +dependencies = [ + "base-db", + "cfg", + "hir-expand", + "rustc-hash", + "span", + "stdx", + "test-utils", + "tt", +] + [[package]] name = "test-utils" version = "0.0.0" @@ -1998,6 +2036,7 @@ name = "tt" version = "0.0.0" dependencies = [ "smol_str", + "span", "stdx", "text-size", ] diff --git a/Cargo.toml b/Cargo.toml index 1213979c390f..7054020086ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [workspace] members = ["xtask/", "lib/*", "crates/*"] -exclude = ["crates/proc-macro-test/imp"] +exclude = ["crates/proc-macro-srv/proc-macro-test/"] resolver = "2" [workspace.package] -rust-version = "1.70" +rust-version = "1.74" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] @@ -70,10 +70,9 @@ proc-macro-srv = { path = "./crates/proc-macro-srv", version = "0.0.0" } proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" } profile = { path = "./crates/profile", version = "0.0.0" } project-model = { path = "./crates/project-model", version = "0.0.0" } -sourcegen = { path = "./crates/sourcegen", version = "0.0.0" } +span = { path = "./crates/span", version = "0.0.0" } stdx = { path = "./crates/stdx", version = "0.0.0" } syntax = { path = "./crates/syntax", version = "0.0.0" } -test-utils = { path = "./crates/test-utils", version = "0.0.0" } text-edit = { path = "./crates/text-edit", version = "0.0.0" } toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } @@ -82,19 +81,25 @@ vfs = { path = "./crates/vfs", version = "0.0.0" } rustc-dependencies = { path = "./crates/rustc-dependencies", version = "0.0.0" } # local crates that aren't published to crates.io. These should not have versions. -proc-macro-test = { path = "./crates/proc-macro-test" } +sourcegen = { path = "./crates/sourcegen" } +test-fixture = { path = "./crates/test-fixture" } +test-utils = { path = "./crates/test-utils" } # In-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.1" } la-arena = { version = "0.3.1" } -lsp-server = { version = "0.7.4" } +lsp-server = { version = "0.7.6" } # non-local crates anyhow = "1.0.75" +arrayvec = "0.7.4" bitflags = "2.4.1" cargo_metadata = "0.18.1" +command-group = "2.0.1" +crossbeam-channel = "0.5.8" dissimilar = "1.0.7" either = "1.9.0" +expect-test = "1.4.0" hashbrown = { version = "0.14", features = [ "inline-more", ], default-features = false } @@ -105,6 +110,7 @@ nohash-hasher = "0.2.0" rayon = "1.8.0" rust-analyzer-salsa = "0.17.0-pre.4" rustc-hash = "1.1.0" +semver = "1.0.14" serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.108" smallvec = { version = "1.10.0", features = [ @@ -124,5 +130,12 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features = triomphe = { version = "0.1.10", default-features = false, features = ["std"] } xshell = "0.2.5" + # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=5.5.3", features = ["raw-api"] } + +[workspace.lints.clippy] +collapsible_if = "allow" +needless_pass_by_value = "allow" +nonminimal_bool = "allow" +redundant_pattern_matching = "allow" \ No newline at end of file diff --git a/crates/base-db/Cargo.toml b/crates/base-db/Cargo.toml index 393ffe155ba8..1aa43175f90b 100644 --- a/crates/base-db/Cargo.toml +++ b/crates/base-db/Cargo.toml @@ -16,12 +16,15 @@ la-arena.workspace = true rust-analyzer-salsa.workspace = true rustc-hash.workspace = true triomphe.workspace = true +semver.workspace = true # local deps cfg.workspace = true profile.workspace = true stdx.workspace = true syntax.workspace = true -test-utils.workspace = true -tt.workspace = true vfs.workspace = true +span.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/base-db/src/change.rs b/crates/base-db/src/change.rs index 6a3b36b23128..4332e572e209 100644 --- a/crates/base-db/src/change.rs +++ b/crates/base-db/src/change.rs @@ -7,18 +7,17 @@ use salsa::Durability; use triomphe::Arc; use vfs::FileId; -use crate::{CrateGraph, ProcMacros, SourceDatabaseExt, SourceRoot, SourceRootId}; +use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId}; /// Encapsulate a bunch of raw `.set` calls on the database. #[derive(Default)] -pub struct Change { +pub struct FileChange { pub roots: Option>, pub files_changed: Vec<(FileId, Option>)>, pub crate_graph: Option, - pub proc_macros: Option, } -impl fmt::Debug for Change { +impl fmt::Debug for FileChange { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = fmt.debug_struct("Change"); if let Some(roots) = &self.roots { @@ -34,9 +33,9 @@ impl fmt::Debug for Change { } } -impl Change { +impl FileChange { pub fn new() -> Self { - Change::default() + FileChange::default() } pub fn set_roots(&mut self, roots: Vec) { @@ -51,10 +50,6 @@ impl Change { self.crate_graph = Some(graph); } - pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) { - self.proc_macros = Some(proc_macros); - } - pub fn apply(self, db: &mut dyn SourceDatabaseExt) { let _p = profile::span("RootDatabase::apply_change"); if let Some(roots) = self.roots { @@ -79,9 +74,6 @@ impl Change { if let Some(crate_graph) = self.crate_graph { db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH); } - if let Some(proc_macros) = self.proc_macros { - db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH); - } } } diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index c2472363aacd..e45a81238ac9 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -6,22 +6,19 @@ //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO is done and lowered to input. -use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync}; +use std::{fmt, mem, ops, str::FromStr}; use cfg::CfgOptions; use la_arena::{Arena, Idx}; use rustc_hash::{FxHashMap, FxHashSet}; +use semver::Version; use syntax::SmolStr; use triomphe::Arc; use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; -use crate::span::SpanData; - // Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`, // then the crate for the proc-macro hasn't been build yet as the build data is missing. pub type ProcMacroPaths = FxHashMap, AbsPathBuf), String>>; -pub type ProcMacros = FxHashMap; - /// Files are grouped into source roots. A source root is a directory on the /// file systems which is watched for changes. Typically it corresponds to a /// Rust crate. Source roots *might* be nested: in this case, a file belongs to @@ -242,49 +239,8 @@ impl CrateDisplayName { CrateDisplayName { crate_name, canonical_name } } } - -// FIXME: These should not be defined in here? Why does base db know about proc-macros -// ProcMacroKind is used in [`fixture`], but that module probably shouldn't be in this crate either. - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct ProcMacroId(pub u32); - -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] -pub enum ProcMacroKind { - CustomDerive, - FuncLike, - Attr, -} - -pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { - fn expand( - &self, - subtree: &tt::Subtree, - attrs: Option<&tt::Subtree>, - env: &Env, - def_site: SpanData, - call_site: SpanData, - mixed_site: SpanData, - ) -> Result, ProcMacroExpansionError>; -} - -#[derive(Debug)] -pub enum ProcMacroExpansionError { - Panic(String), - /// Things like "proc macro server was killed by OOM". - System(String), -} - -pub type ProcMacroLoadResult = Result, String>; pub type TargetLayoutLoadResult = Result, Arc>; -#[derive(Debug, Clone)] -pub struct ProcMacro { - pub name: SmolStr, - pub kind: ProcMacroKind, - pub expander: sync::Arc, -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ReleaseChannel { Stable, @@ -303,7 +259,7 @@ impl ReleaseChannel { pub fn from_str(str: &str) -> Option { Some(match str { - "" => ReleaseChannel::Stable, + "" | "stable" => ReleaseChannel::Stable, "nightly" => ReleaseChannel::Nightly, _ if str.starts_with("beta") => ReleaseChannel::Beta, _ => return None, @@ -334,7 +290,7 @@ pub struct CrateData { // things. This info does need to be somewhat present though as to prevent deduplication from // happening across different workspaces with different layouts. pub target_layout: TargetLayoutLoadResult, - pub channel: Option, + pub toolchain: Option, } impl CrateData { @@ -391,6 +347,10 @@ impl CrateData { slf_deps.eq(other_deps) } + + pub fn channel(&self) -> Option { + self.toolchain.as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre)) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -398,10 +358,12 @@ pub enum Edition { Edition2015, Edition2018, Edition2021, + Edition2024, } impl Edition { pub const CURRENT: Edition = Edition::Edition2021; + pub const DEFAULT: Edition = Edition::Edition2015; } #[derive(Default, Debug, Clone, PartialEq, Eq)] @@ -472,7 +434,7 @@ impl CrateGraph { is_proc_macro: bool, origin: CrateOrigin, target_layout: Result, Arc>, - channel: Option, + toolchain: Option, ) -> CrateId { let data = CrateData { root_file_id, @@ -486,7 +448,7 @@ impl CrateGraph { origin, target_layout, is_proc_macro, - channel, + toolchain, }; self.arena.alloc(data) } @@ -784,6 +746,7 @@ impl FromStr for Edition { "2015" => Edition::Edition2015, "2018" => Edition::Edition2018, "2021" => Edition::Edition2021, + "2024" => Edition::Edition2024, _ => return Err(ParseEditionError { invalid_input: s.to_string() }), }; Ok(res) @@ -796,6 +759,7 @@ impl fmt::Display for Edition { Edition::Edition2015 => "2015", Edition::Edition2018 => "2018", Edition::Edition2021 => "2021", + Edition::Edition2024 => "2024", }) } } diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 57e7934367bb..a0a55df5f99a 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -4,27 +4,27 @@ mod input; mod change; -pub mod fixture; -pub mod span; use std::panic; use rustc_hash::FxHashSet; -use syntax::{ast, Parse, SourceFile, TextRange, TextSize}; +use syntax::{ast, Parse, SourceFile}; use triomphe::Arc; pub use crate::{ - change::Change, + change::FileChange, input::{ CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, - DependencyKind, Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, - ProcMacroExpansionError, ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, - ProcMacros, ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult, + DependencyKind, Edition, Env, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, + SourceRootId, TargetLayoutLoadResult, }, }; pub use salsa::{self, Cancelled}; +pub use span::{FilePosition, FileRange}; pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath}; +pub use semver::{BuildMetadata, Prerelease, Version, VersionReq}; + #[macro_export] macro_rules! impl_intern_key { ($name:ident) => { @@ -43,18 +43,6 @@ pub trait Upcast { fn upcast(&self) -> &T; } -#[derive(Clone, Copy, Debug)] -pub struct FilePosition { - pub file_id: FileId, - pub offset: TextSize, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub struct FileRange { - pub file_id: FileId, - pub range: TextRange, -} - pub const DEFAULT_PARSE_LRU_CAP: usize = 128; pub trait FileLoader { @@ -74,10 +62,6 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug { /// The crate graph. #[salsa::input] fn crate_graph(&self) -> Arc; - - /// The proc macros. - #[salsa::input] - fn proc_macros(&self) -> Arc; } fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse { diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml index 4324584df39d..fbda065b10f3 100644 --- a/crates/cfg/Cargo.toml +++ b/crates/cfg/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true doctest = false [dependencies] -rustc-hash = "1.1.0" +rustc-hash.workspace = true # locals deps tt.workspace = true @@ -29,3 +29,6 @@ derive_arbitrary = "1.3.2" # local deps mbe.workspace = true syntax.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs index c7ac1af934a0..62fb429a63fa 100644 --- a/crates/cfg/src/tests.rs +++ b/crates/cfg/src/tests.rs @@ -1,6 +1,6 @@ use arbitrary::{Arbitrary, Unstructured}; use expect_test::{expect, Expect}; -use mbe::{syntax_node_to_token_tree, DummyTestSpanMap}; +use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; use syntax::{ast, AstNode}; use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; @@ -8,7 +8,7 @@ use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; fn assert_parse_result(input: &str, expected: CfgExpr) { let source_file = ast::SourceFile::parse(input).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap); + let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let cfg = CfgExpr::parse(&tt); assert_eq!(cfg, expected); } @@ -16,7 +16,7 @@ fn assert_parse_result(input: &str, expected: CfgExpr) { fn check_dnf(input: &str, expect: Expect) { let source_file = ast::SourceFile::parse(input).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap); + let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let cfg = CfgExpr::parse(&tt); let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); expect.assert_eq(&actual); @@ -25,7 +25,7 @@ fn check_dnf(input: &str, expect: Expect) { fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { let source_file = ast::SourceFile::parse(input).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap); + let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let cfg = CfgExpr::parse(&tt); let dnf = DnfExpr::new(cfg); let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); @@ -36,7 +36,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { let source_file = ast::SourceFile::parse(input).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap); + let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let cfg = CfgExpr::parse(&tt); let dnf = DnfExpr::new(cfg); let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::>(); diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 4322d2d966a1..b8c10da1b6e3 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml @@ -13,14 +13,17 @@ doctest = false [dependencies] cargo_metadata.workspace = true -crossbeam-channel = "0.5.8" +crossbeam-channel.workspace = true tracing.workspace = true -rustc-hash = "1.1.0" +rustc-hash.workspace = true serde_json.workspace = true serde.workspace = true -command-group = "2.0.1" +command-group.workspace = true # local deps paths.workspace = true stdx.workspace = true toolchain.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml index 2d174517605f..5933d30040fa 100644 --- a/crates/hir-def/Cargo.toml +++ b/crates/hir-def/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true doctest = false [dependencies] -arrayvec = "0.7.2" +arrayvec.workspace = true bitflags.workspace = true cov-mark = "2.0.0-pre.1" dashmap.workspace = true @@ -23,7 +23,7 @@ indexmap.workspace = true itertools.workspace = true la-arena.workspace = true once_cell = "1.17.0" -rustc-hash = "1.1.0" +rustc-hash.workspace = true tracing.workspace = true smallvec.workspace = true hashbrown.workspace = true @@ -42,13 +42,18 @@ mbe.workspace = true cfg.workspace = true tt.workspace = true limit.workspace = true +span.workspace = true [dev-dependencies] -expect-test = "1.4.0" +expect-test.workspace = true # local deps test-utils.workspace = true +test-fixture.workspace = true [features] in-rust-tree = ["rustc-dependencies/in-rust-tree"] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index 942b28fc1450..26f76afb1f09 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -637,9 +637,12 @@ impl<'attr> AttrQuery<'attr> { } } -fn any_has_attrs( - db: &dyn DefDatabase, - id: impl Lookup>, +fn any_has_attrs<'db>( + db: &(dyn DefDatabase + 'db), + id: impl Lookup< + Database<'db> = dyn DefDatabase + 'db, + Data = impl HasSource, + >, ) -> InFile { id.lookup(db).source(db).map(ast::AnyHasAttrs::new) } @@ -650,17 +653,17 @@ fn attrs_from_item_tree(db: &dyn DefDatabase, id: ItemTreeId tree.raw_attrs(mod_item.into()).clone() } -fn attrs_from_item_tree_loc( - db: &dyn DefDatabase, - lookup: impl Lookup>, +fn attrs_from_item_tree_loc<'db, N: ItemTreeNode>( + db: &(dyn DefDatabase + 'db), + lookup: impl Lookup = dyn DefDatabase + 'db, Data = ItemLoc>, ) -> RawAttrs { let id = lookup.lookup(db).id; attrs_from_item_tree(db, id) } -fn attrs_from_item_tree_assoc( - db: &dyn DefDatabase, - lookup: impl Lookup>, +fn attrs_from_item_tree_assoc<'db, N: ItemTreeNode>( + db: &(dyn DefDatabase + 'db), + lookup: impl Lookup = dyn DefDatabase + 'db, Data = AssocItemLoc>, ) -> RawAttrs { let id = lookup.lookup(db).id; attrs_from_item_tree(db, id) diff --git a/crates/hir-def/src/attr/tests.rs b/crates/hir-def/src/attr/tests.rs index 0f98a4ec93c6..1a63e96bfa92 100644 --- a/crates/hir-def/src/attr/tests.rs +++ b/crates/hir-def/src/attr/tests.rs @@ -1,19 +1,23 @@ //! This module contains tests for doc-expression parsing. //! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`. +use triomphe::Arc; + use base_db::FileId; -use hir_expand::span::{RealSpanMap, SpanMapRef}; +use hir_expand::span_map::{RealSpanMap, SpanMap}; use mbe::syntax_node_to_token_tree; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, TextRange}; use crate::attr::{DocAtom, DocExpr}; fn assert_parse_result(input: &str, expected: DocExpr) { let source_file = ast::SourceFile::parse(input).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0)))); let tt = syntax_node_to_token_tree( tt.syntax(), - SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId::from_raw(0))), + map.as_ref(), + map.span_for_range(TextRange::empty(0.into())), ); let cfg = DocExpr::parse(&tt); assert_eq!(cfg, expected); diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index c6a909320159..a45ec844aba0 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -8,7 +8,7 @@ use either::Either; use hir_expand::{ ast_id_map::AstIdMap, name::{name, AsName, Name}, - AstId, ExpandError, InFile, + ExpandError, InFile, }; use intern::Interned; use profile::Count; @@ -66,7 +66,7 @@ pub(super) fn lower( krate, def_map: expander.module.def_map(db), source_map: BodySourceMap::default(), - ast_id_map: db.ast_id_map(expander.current_file_id), + ast_id_map: db.ast_id_map(expander.current_file_id()), body: Body { exprs: Default::default(), pats: Default::default(), @@ -408,7 +408,7 @@ impl ExprCollector<'_> { ast::Expr::ParenExpr(e) => { let inner = self.collect_expr_opt(e.expr()); // make the paren expr point to the inner expression as well - let src = self.expander.to_source(syntax_ptr); + let src = self.expander.in_file(syntax_ptr); self.source_map.expr_map.insert(src, inner); inner } @@ -441,7 +441,7 @@ impl ExprCollector<'_> { Some(e) => self.collect_expr(e), None => self.missing_expr(), }; - let src = self.expander.to_source(AstPtr::new(&field)); + let src = self.expander.in_file(AstPtr::new(&field)); self.source_map.field_map_back.insert(expr, src); Some(RecordLitField { name, expr }) }) @@ -644,7 +644,7 @@ impl ExprCollector<'_> { Some(id) => { // Make the macro-call point to its expanded expression so we can query // semantics on syntax pointers to the macro - let src = self.expander.to_source(syntax_ptr); + let src = self.expander.in_file(syntax_ptr); self.source_map.expr_map.insert(src, id); id } @@ -957,22 +957,31 @@ impl ExprCollector<'_> { T: ast::AstNode, { // File containing the macro call. Expansion errors will be attached here. - let outer_file = self.expander.current_file_id; + let outer_file = self.expander.current_file_id(); - let macro_call_ptr = self.expander.to_source(AstPtr::new(&mcall)); + let macro_call_ptr = self.expander.in_file(syntax_ptr); let module = self.expander.module.local_id; - let res = self.expander.enter_expand(self.db, mcall, |path| { - self.def_map - .resolve_path( - self.db, - module, - &path, - crate::item_scope::BuiltinShadowMode::Other, - Some(MacroSubNs::Bang), - ) - .0 - .take_macros() - }); + + let res = match self.def_map.modules[module] + .scope + .macro_invocations + .get(&InFile::new(outer_file, self.ast_id_map.ast_id_for_ptr(syntax_ptr))) + { + // fast path, macro call is in a block module + Some(&call) => Ok(self.expander.enter_expand_id(self.db, call)), + None => self.expander.enter_expand(self.db, mcall, |path| { + self.def_map + .resolve_path( + self.db, + module, + &path, + crate::item_scope::BuiltinShadowMode::Other, + Some(MacroSubNs::Bang), + ) + .0 + .take_macros() + }), + }; let res = match res { Ok(res) => res, @@ -986,7 +995,6 @@ impl ExprCollector<'_> { return collector(self, None); } }; - if record_diagnostics { match &res.err { Some(ExpandError::UnresolvedProcMacro(krate)) => { @@ -1013,10 +1021,10 @@ impl ExprCollector<'_> { Some((mark, expansion)) => { // Keep collecting even with expansion errors so we can provide completions and // other services in incomplete macro expressions. - self.source_map.expansions.insert(macro_call_ptr, self.expander.current_file_id); + self.source_map.expansions.insert(macro_call_ptr, self.expander.current_file_id()); let prev_ast_id_map = mem::replace( &mut self.ast_id_map, - self.db.ast_id_map(self.expander.current_file_id), + self.db.ast_id_map(self.expander.current_file_id()), ); if record_diagnostics { @@ -1066,7 +1074,7 @@ impl ExprCollector<'_> { Some(tail) => { // Make the macro-call point to its expanded expression so we can query // semantics on syntax pointers to the macro - let src = self.expander.to_source(syntax_ptr); + let src = self.expander.in_file(syntax_ptr); self.source_map.expr_map.insert(src, tail); Some(tail) } @@ -1140,7 +1148,7 @@ impl ExprCollector<'_> { let block_id = if block_has_items { let file_local_id = self.ast_id_map.ast_id(&block); - let ast_id = AstId::new(self.expander.current_file_id, file_local_id); + let ast_id = self.expander.in_file(file_local_id); Some(self.db.intern_block(BlockLoc { ast_id, module: self.expander.module })) } else { None @@ -1333,7 +1341,7 @@ impl ExprCollector<'_> { let ast_pat = f.pat()?; let pat = self.collect_pat(ast_pat, binding_list); let name = f.field_name()?.as_name(); - let src = self.expander.to_source(AstPtr::new(&f)); + let src = self.expander.in_file(AstPtr::new(&f)); self.source_map.pat_field_map_back.insert(pat, src); Some(RecordFieldPat { name, pat }) }) @@ -1391,7 +1399,7 @@ impl ExprCollector<'_> { ast::Pat::MacroPat(mac) => match mac.macro_call() { Some(call) => { let macro_ptr = AstPtr::new(&call); - let src = self.expander.to_source(AstPtr::new(&Either::Left(pat))); + let src = self.expander.in_file(AstPtr::new(&Either::Left(pat))); let pat = self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { this.collect_pat_opt(expanded_pat, binding_list) @@ -1472,10 +1480,7 @@ impl ExprCollector<'_> { } self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode { - node: InFile::new( - self.expander.current_file_id, - SyntaxNodePtr::new(owner.syntax()), - ), + node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())), cfg, opts: self.expander.cfg_options().clone(), }); @@ -1514,10 +1519,7 @@ impl ExprCollector<'_> { } else { Err(BodyDiagnostic::UnreachableLabel { name, - node: InFile::new( - self.expander.current_file_id, - AstPtr::new(&lifetime), - ), + node: self.expander.in_file(AstPtr::new(&lifetime)), }) }; } @@ -1526,7 +1528,7 @@ impl ExprCollector<'_> { Err(BodyDiagnostic::UndeclaredLabel { name, - node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)), + node: self.expander.in_file(AstPtr::new(&lifetime)), }) } @@ -1990,7 +1992,7 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> impl ExprCollector<'_> { fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { - let src = self.expander.to_source(ptr); + let src = self.expander.in_file(ptr); let id = self.body.exprs.alloc(expr); self.source_map.expr_map_back.insert(id, src.clone()); self.source_map.expr_map.insert(src, id); @@ -2018,7 +2020,7 @@ impl ExprCollector<'_> { } fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId { - let src = self.expander.to_source(ptr); + let src = self.expander.in_file(ptr); let id = self.body.pats.alloc(pat); self.source_map.pat_map_back.insert(id, src.clone()); self.source_map.pat_map.insert(src, id); @@ -2033,7 +2035,7 @@ impl ExprCollector<'_> { } fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId { - let src = self.expander.to_source(ptr); + let src = self.expander.in_file(ptr); let id = self.body.labels.alloc(label); self.source_map.label_map_back.insert(id, src.clone()); self.source_map.label_map.insert(src, id); diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs index baca293e2904..ab623250d407 100644 --- a/crates/hir-def/src/body/scope.rs +++ b/crates/hir-def/src/body/scope.rs @@ -267,9 +267,10 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope #[cfg(test)] mod tests { - use base_db::{fixture::WithFixture, FileId, SourceDatabase}; + use base_db::{FileId, SourceDatabase}; use hir_expand::{name::AsName, InFile}; use syntax::{algo::find_node_at_offset, ast, AstNode}; + use test_fixture::WithFixture; use test_utils::{assert_eq_text, extract_offset}; use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs index 2b432dfbb92b..a76ddffb4116 100644 --- a/crates/hir-def/src/body/tests.rs +++ b/crates/hir-def/src/body/tests.rs @@ -1,7 +1,8 @@ mod block; -use base_db::{fixture::WithFixture, SourceDatabase}; +use base_db::SourceDatabase; use expect_test::{expect, Expect}; +use test_fixture::WithFixture; use crate::{test_db::TestDB, ModuleDefId}; diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 635d13f24ad8..9c183c9332ba 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -16,7 +16,7 @@ use crate::{ db::DefDatabase, expander::{Expander, Mark}, item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, TreeId}, - macro_call_as_call_id, macro_id_to_def_id, + macro_call_as_call_id, nameres::{ attr_resolution::ResolvedAttr, diagnostics::DefDiagnostic, @@ -720,7 +720,7 @@ impl<'a> AssocItemCollector<'a> { ) .0 .take_macros() - .map(|it| macro_id_to_def_id(self.db, it)) + .map(|it| self.db.macro_def(it)) }; match macro_call_as_call_id( self.db.upcast(), diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 31c1a713031c..d5831022f28f 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -1,7 +1,7 @@ //! Defines database & queries for name resolution. use base_db::{salsa, CrateId, SourceDatabase, Upcast}; use either::Either; -use hir_expand::{db::ExpandDatabase, HirFileId}; +use hir_expand::{db::ExpandDatabase, HirFileId, MacroDefId}; use intern::Interned; use la_arena::ArenaMap; use syntax::{ast, AstPtr}; @@ -24,9 +24,10 @@ use crate::{ AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, InTypeConstId, InTypeConstLoc, LocalEnumVariantId, - LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc, - StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, - TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId, + LocalFieldId, Macro2Id, Macro2Loc, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, + ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, + TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, + VariantId, }; #[salsa::query_group(InternDatabaseStorage)] @@ -110,6 +111,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; + fn macro_def(&self, m: MacroId) -> MacroDefId; + // region:data #[salsa::invoke(StructData::struct_data_query)] @@ -239,12 +242,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; - #[salsa::transparent] - fn crate_limits(&self, crate_id: CrateId) -> CrateLimits; - - #[salsa::transparent] - fn recursion_limit(&self, crate_id: CrateId) -> u32; - fn crate_supports_no_std(&self, crate_id: CrateId) -> bool; } @@ -253,24 +250,6 @@ fn crate_def_map_wait(db: &dyn DefDatabase, krate: CrateId) -> Arc { db.crate_def_map_query(krate) } -pub struct CrateLimits { - /// The maximum depth for potentially infinitely-recursive compile-time operations like macro expansion or auto-dereference. - pub recursion_limit: u32, -} - -fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits { - let def_map = db.crate_def_map(crate_id); - - CrateLimits { - // 128 is the default in rustc. - recursion_limit: def_map.recursion_limit().unwrap_or(128), - } -} - -fn recursion_limit(db: &dyn DefDatabase, crate_id: CrateId) -> u32 { - db.crate_limits(crate_id).recursion_limit -} - fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { let file = db.crate_graph()[crate_id].root_file_id; let item_tree = db.file_item_tree(file.into()); @@ -305,3 +284,78 @@ fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { false } + +fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { + use hir_expand::InFile; + + use crate::{Lookup, MacroDefKind, MacroExpander}; + + let kind = |expander, file_id, m| { + let in_file = InFile::new(file_id, m); + match expander { + MacroExpander::Declarative => MacroDefKind::Declarative(in_file), + MacroExpander::BuiltIn(it) => MacroDefKind::BuiltIn(it, in_file), + MacroExpander::BuiltInAttr(it) => MacroDefKind::BuiltInAttr(it, in_file), + MacroExpander::BuiltInDerive(it) => MacroDefKind::BuiltInDerive(it, in_file), + MacroExpander::BuiltInEager(it) => MacroDefKind::BuiltInEager(it, in_file), + } + }; + + match id { + MacroId::Macro2Id(it) => { + let loc: Macro2Loc = it.lookup(db); + + let item_tree = loc.id.item_tree(db); + let makro = &item_tree[loc.id.value]; + MacroDefId { + krate: loc.container.krate, + kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()), + local_inner: false, + allow_internal_unsafe: loc.allow_internal_unsafe, + span: db + .span_map(loc.id.file_id()) + .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()), + edition: loc.edition, + } + } + + MacroId::MacroRulesId(it) => { + let loc: MacroRulesLoc = it.lookup(db); + + let item_tree = loc.id.item_tree(db); + let makro = &item_tree[loc.id.value]; + MacroDefId { + krate: loc.container.krate, + kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()), + local_inner: loc.flags.contains(MacroRulesLocFlags::LOCAL_INNER), + allow_internal_unsafe: loc + .flags + .contains(MacroRulesLocFlags::ALLOW_INTERNAL_UNSAFE), + span: db + .span_map(loc.id.file_id()) + .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()), + edition: loc.edition, + } + } + MacroId::ProcMacroId(it) => { + let loc = it.lookup(db); + + let item_tree = loc.id.item_tree(db); + let makro = &item_tree[loc.id.value]; + MacroDefId { + krate: loc.container.krate, + kind: MacroDefKind::ProcMacro( + loc.expander, + loc.kind, + InFile::new(loc.id.file_id(), makro.ast_id), + ), + local_inner: false, + allow_internal_unsafe: false, + span: db + .span_map(loc.id.file_id()) + .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()), + edition: loc.edition, + } + } + } +} diff --git a/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs index 398f116d8313..b83feeedc34c 100644 --- a/crates/hir-def/src/expander.rs +++ b/crates/hir-def/src/expander.rs @@ -4,15 +4,15 @@ use base_db::CrateId; use cfg::CfgOptions; use drop_bomb::DropBomb; use hir_expand::{ - attrs::RawAttrs, mod_path::ModPath, span::SpanMap, ExpandError, ExpandResult, HirFileId, + attrs::RawAttrs, mod_path::ModPath, span_map::SpanMap, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId, }; use limit::Limit; -use syntax::{ast, Parse, SyntaxNode}; +use syntax::{ast, Parse}; use crate::{ - attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall, - MacroId, ModuleId, UnresolvedMacro, + attr::Attrs, db::DefDatabase, lower::LowerCtx, path::Path, AsMacroCall, MacroId, ModuleId, + UnresolvedMacro, }; #[derive(Debug)] @@ -20,7 +20,7 @@ pub struct Expander { cfg_options: CfgOptions, span_map: SpanMap, krate: CrateId, - pub(crate) current_file_id: HirFileId, + current_file_id: HirFileId, pub(crate) module: ModuleId, /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. recursion_depth: u32, @@ -29,12 +29,13 @@ pub struct Expander { impl Expander { pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { - let recursion_limit = db.recursion_limit(module.krate); - #[cfg(not(test))] - let recursion_limit = Limit::new(recursion_limit as usize); - // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug - #[cfg(test)] - let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize)); + let recursion_limit = module.def_map(db).recursion_limit() as usize; + let recursion_limit = Limit::new(if cfg!(test) { + // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug + std::cmp::min(32, recursion_limit) + } else { + recursion_limit + }); Expander { current_file_id, module, @@ -56,9 +57,9 @@ impl Expander { let mut unresolved_macro_err = None; let result = self.within_limit(db, |this| { - let macro_call = InFile::new(this.current_file_id, ¯o_call); + let macro_call = this.in_file(¯o_call); match macro_call.as_call_id_with_errors(db.upcast(), this.module.krate(), |path| { - resolver(path).map(|it| macro_id_to_def_id(db, it)) + resolver(path).map(|it| db.macro_def(it)) }) { Ok(call_id) => call_id, Err(resolve_err) => { @@ -83,17 +84,6 @@ impl Expander { self.within_limit(db, |_this| ExpandResult::ok(Some(call_id))) } - fn enter_expand_inner( - db: &dyn DefDatabase, - call_id: MacroCallId, - error: Option, - ) -> ExpandResult>>> { - let macro_file = call_id.as_macro_file(); - let ExpandResult { value, err } = db.parse_macro_expansion(macro_file); - - ExpandResult { value: Some(InFile::new(macro_file.into(), value.0)), err: error.or(err) } - } - pub fn exit(&mut self, mut mark: Mark) { self.span_map = mark.span_map; self.current_file_id = mark.file_id; @@ -113,7 +103,7 @@ impl Expander { LowerCtx::new(db, self.span_map.clone(), self.current_file_id) } - pub(crate) fn to_source(&self, value: T) -> InFile { + pub(crate) fn in_file(&self, value: T) -> InFile { InFile { file_id: self.current_file_id, value } } @@ -164,26 +154,34 @@ impl Expander { return ExpandResult { value: None, err }; }; - let res = Self::enter_expand_inner(db, call_id, err); - match res.err { - // If proc-macro is disabled or unresolved, we want to expand to a missing expression - // instead of an empty tree which might end up in an empty block. - Some(ExpandError::UnresolvedProcMacro(_)) => res.map(|_| None), - _ => res.map(|value| { - value.and_then(|InFile { file_id, value }| { - let parse = value.cast::()?; + let macro_file = call_id.as_macro_file(); + let res = db.parse_macro_expansion(macro_file); + + let err = err.or(res.err); + ExpandResult { + value: match err { + // If proc-macro is disabled or unresolved, we want to expand to a missing expression + // instead of an empty tree which might end up in an empty block. + Some(ExpandError::UnresolvedProcMacro(_)) => None, + _ => (|| { + let parse = res.value.0.cast::()?; self.recursion_depth += 1; - let old_span_map = std::mem::replace(&mut self.span_map, db.span_map(file_id)); - let old_file_id = std::mem::replace(&mut self.current_file_id, file_id); + let old_span_map = std::mem::replace( + &mut self.span_map, + SpanMap::ExpansionSpanMap(res.value.1), + ); + let old_file_id = + std::mem::replace(&mut self.current_file_id, macro_file.into()); let mark = Mark { file_id: old_file_id, span_map: old_span_map, bomb: DropBomb::new("expansion mark dropped"), }; Some((mark, parse)) - }) - }), + })(), + }, + err, } } } diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index 13af0b0218e8..4737b48703db 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -585,9 +585,9 @@ fn find_local_import_locations( #[cfg(test)] mod tests { - use base_db::fixture::WithFixture; use hir_expand::db::ExpandDatabase; use syntax::ast::AstNode; + use test_fixture::WithFixture; use crate::test_db::TestDB; diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 26d333f9a0b0..aea7229bd648 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -469,8 +469,9 @@ pub fn search_dependencies( #[cfg(test)] mod tests { - use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; + use base_db::{SourceDatabase, Upcast}; use expect_test::{expect, Expect}; + use test_fixture::WithFixture; use crate::{db::DefDatabase, test_db::TestDB, ItemContainerId, Lookup}; diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index ce83cb435e2e..4902f24e2e3a 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -102,8 +102,10 @@ pub struct ItemScope { // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will // be all resolved to the last one defined if shadowing happens. legacy_macros: FxHashMap>, - /// The derive macro invocations in this scope. + /// The attribute macro invocations in this scope. attr_macros: FxHashMap, MacroCallId>, + /// The macro invocations in this scope. + pub macro_invocations: FxHashMap, MacroCallId>, /// The derive macro invocations in this scope, keyed by the owner item over the actual derive attributes /// paired with the derive macro invocations for the specific attribute. derive_macros: FxHashMap, SmallVec<[DeriveMacroInvocation; 1]>>, @@ -345,6 +347,10 @@ impl ItemScope { self.attr_macros.insert(item, call); } + pub(crate) fn add_macro_invoc(&mut self, call: AstId, call_id: MacroCallId) { + self.macro_invocations.insert(call, call_id); + } + pub(crate) fn attr_macro_invocs( &self, ) -> impl Iterator, MacroCallId)> + '_ { @@ -692,6 +698,7 @@ impl ItemScope { use_imports_values, use_imports_types, use_imports_macros, + macro_invocations, } = self; types.shrink_to_fit(); values.shrink_to_fit(); @@ -709,6 +716,7 @@ impl ItemScope { derive_macros.shrink_to_fit(); extern_crate_decls.shrink_to_fit(); use_decls.shrink_to_fit(); + macro_invocations.shrink_to_fit(); } } diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 3d2cddffa3ba..20e4e44339e9 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -29,6 +29,9 @@ //! //! In general, any item in the `ItemTree` stores its `AstId`, which allows mapping it back to its //! surface syntax. +//! +//! Note that we cannot store [`span::Span`]s inside of this, as typing in an item invalidates its +//! encompassing span! mod lower; mod pretty; @@ -42,7 +45,7 @@ use std::{ }; use ast::{AstNode, HasName, StructKind}; -use base_db::{span::SyntaxContextId, CrateId}; +use base_db::CrateId; use either::Either; use hir_expand::{ ast_id_map::{AstIdNode, FileAstId}, @@ -55,6 +58,7 @@ use la_arena::{Arena, Idx, IdxRange, RawIdx}; use profile::Count; use rustc_hash::FxHashMap; use smallvec::SmallVec; +use span::Span; use stdx::never; use syntax::{ast, match_ast, SyntaxKind}; use triomphe::Arc; @@ -280,7 +284,7 @@ struct ItemTreeData { mods: Arena, macro_calls: Arena, macro_rules: Arena, - macro_defs: Arena, + macro_defs: Arena, vis: ItemVisibilities, } @@ -513,7 +517,7 @@ mod_items! { Mod in mods -> ast::Module, MacroCall in macro_calls -> ast::MacroCall, MacroRules in macro_rules -> ast::MacroRules, - MacroDef in macro_defs -> ast::MacroDef, + Macro2 in macro_defs -> ast::MacroDef, } macro_rules! impl_index { @@ -746,7 +750,8 @@ pub struct MacroCall { pub path: Interned, pub ast_id: FileAstId, pub expand_to: ExpandTo, - pub call_site: SyntaxContextId, + // FIXME: We need to move this out. It invalidates the item tree when typing inside the macro call. + pub call_site: Span, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -758,7 +763,7 @@ pub struct MacroRules { /// "Macros 2.0" macro definition. #[derive(Debug, Clone, Eq, PartialEq)] -pub struct MacroDef { +pub struct Macro2 { pub name: Name, pub visibility: RawVisibilityId, pub ast_id: FileAstId, @@ -917,7 +922,7 @@ impl ModItem { | ModItem::Impl(_) | ModItem::Mod(_) | ModItem::MacroRules(_) - | ModItem::MacroDef(_) => None, + | ModItem::Macro2(_) => None, ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)), ModItem::Const(konst) => Some(AssocItem::Const(*konst)), ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)), @@ -943,7 +948,7 @@ impl ModItem { ModItem::Mod(it) => tree[it.index()].ast_id().upcast(), ModItem::MacroCall(it) => tree[it.index()].ast_id().upcast(), ModItem::MacroRules(it) => tree[it.index()].ast_id().upcast(), - ModItem::MacroDef(it) => tree[it.index()].ast_id().upcast(), + ModItem::Macro2(it) => tree[it.index()].ast_id().upcast(), } } } diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 83a2790ce8f1..8e2fafe81b50 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -2,7 +2,7 @@ use std::collections::hash_map::Entry; -use hir_expand::{ast_id_map::AstIdMap, span::SpanMapRef, HirFileId}; +use hir_expand::{ast_id_map::AstIdMap, span_map::SpanMapRef, HirFileId}; use syntax::ast::{self, HasModuleItem, HasTypeBounds}; use crate::{ @@ -549,7 +549,7 @@ impl<'a> Ctx<'a> { path, ast_id, expand_to, - call_site: span_map.span_for_range(m.syntax().text_range()).ctx, + call_site: span_map.span_for_range(m.syntax().text_range()), }; Some(id(self.data().macro_calls.alloc(res))) } @@ -562,13 +562,13 @@ impl<'a> Ctx<'a> { Some(id(self.data().macro_rules.alloc(res))) } - fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option> { + fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option> { let name = m.name().map(|it| it.as_name())?; let ast_id = self.source_ast_id_map.ast_id(m); let visibility = self.lower_visibility(m); - let res = MacroDef { name, ast_id, visibility }; + let res = Macro2 { name, ast_id, visibility }; Some(id(self.data().macro_defs.alloc(res))) } diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index 244111d202ce..6d92fce07272 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -464,8 +464,8 @@ impl Printer<'_> { let MacroRules { name, ast_id: _ } = &self.tree[it]; wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db.upcast())); } - ModItem::MacroDef(it) => { - let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; + ModItem::Macro2(it) => { + let Macro2 { name, visibility, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast())); } diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 96c65b941c1d..f97ae0d8e434 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -1,5 +1,5 @@ -use base_db::fixture::WithFixture; use expect_test::{expect, Expect}; +use test_fixture::WithFixture; use crate::{db::DefDatabase, test_db::TestDB}; diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index b5333861cc8a..22ba3aab4e9f 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -63,7 +63,7 @@ use std::{ panic::{RefUnwindSafe, UnwindSafe}, }; -use base_db::{impl_intern_key, salsa, span::SyntaxContextId, CrateId, ProcMacroKind}; +use base_db::{impl_intern_key, salsa, CrateId, Edition}; use hir_expand::{ ast_id_map::{AstIdNode, FileAstId}, attrs::{Attr, AttrId, AttrInput}, @@ -72,24 +72,27 @@ use hir_expand::{ builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander}, db::ExpandDatabase, eager::expand_eager_macro_input, + impl_intern_lookup, name::Name, - proc_macro::ProcMacroExpander, + proc_macro::{CustomProcMacroExpander, ProcMacroKind}, AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, }; use item_tree::ExternBlock; use la_arena::Idx; use nameres::DefMap; +use span::Span; use stdx::impl_from; use syntax::{ast, AstNode}; -pub use hir_expand::tt; +pub use hir_expand::{tt, Intern, Lookup}; use crate::{ builtin_type::BuiltinType, data::adt::VariantData, + db::DefDatabase, item_tree::{ - Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, + Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, Macro2, MacroRules, Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, }, }; @@ -101,7 +104,7 @@ pub struct CrateRootModuleId { } impl CrateRootModuleId { - pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc { + pub fn def_map(&self, db: &dyn DefDatabase) -> Arc { db.crate_def_map(self.krate) } @@ -163,7 +166,7 @@ pub struct ModuleId { } impl ModuleId { - pub fn def_map(self, db: &dyn db::DefDatabase) -> Arc { + pub fn def_map(self, db: &dyn DefDatabase) -> Arc { match self.block { Some(block) => db.block_def_map(block), None => db.crate_def_map(self.krate), @@ -174,7 +177,7 @@ impl ModuleId { self.krate } - pub fn name(self, db: &dyn db::DefDatabase) -> Option { + pub fn name(self, db: &dyn DefDatabase) -> Option { let def_map = self.def_map(db); let parent = def_map[self.local_id].parent?; def_map[parent].children.iter().find_map(|(name, module_id)| { @@ -186,7 +189,7 @@ impl ModuleId { }) } - pub fn containing_module(self, db: &dyn db::DefDatabase) -> Option { + pub fn containing_module(self, db: &dyn DefDatabase) -> Option { self.def_map(db).containing_module(self.local_id) } @@ -263,20 +266,7 @@ impl Hash for AssocItemLoc { macro_rules! impl_intern { ($id:ident, $loc:ident, $intern:ident, $lookup:ident) => { impl_intern_key!($id); - - impl Intern for $loc { - type ID = $id; - fn intern(self, db: &dyn db::DefDatabase) -> $id { - db.$intern(self) - } - } - - impl Lookup for $id { - type Data = $loc; - fn lookup(&self, db: &dyn db::DefDatabase) -> $loc { - db.$lookup(*self) - } - } + impl_intern_lookup!(DefDatabase, $id, $loc, $intern, $lookup); }; } @@ -376,9 +366,10 @@ pub struct Macro2Id(salsa::InternId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Macro2Loc { pub container: ModuleId, - pub id: ItemTreeId, + pub id: ItemTreeId, pub expander: MacroExpander, pub allow_internal_unsafe: bool, + pub edition: Edition, } impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2); @@ -389,19 +380,28 @@ pub struct MacroRulesLoc { pub container: ModuleId, pub id: ItemTreeId, pub expander: MacroExpander, - pub allow_internal_unsafe: bool, - pub local_inner: bool, + pub flags: MacroRulesLocFlags, + pub edition: Edition, } impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macro_rules); +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct MacroRulesLocFlags: u8 { + const ALLOW_INTERNAL_UNSAFE = 1 << 0; + const LOCAL_INNER = 1 << 1; + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct ProcMacroId(salsa::InternId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ProcMacroLoc { pub container: CrateRootModuleId, pub id: ItemTreeId, - pub expander: ProcMacroExpander, + pub expander: CustomProcMacroExpander, pub kind: ProcMacroKind, + pub edition: Edition, } impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_macro); @@ -510,7 +510,7 @@ pub enum MacroId { impl_from!(Macro2Id, MacroRulesId, ProcMacroId for MacroId); impl MacroId { - pub fn is_attribute(self, db: &dyn db::DefDatabase) -> bool { + pub fn is_attribute(self, db: &dyn DefDatabase) -> bool { matches!(self, MacroId::ProcMacroId(it) if it.lookup(db).kind == ProcMacroKind::Attr) } } @@ -722,7 +722,7 @@ impl PartialEq for InTypeConstLoc { } impl InTypeConstId { - pub fn source(&self, db: &dyn db::DefDatabase) -> ast::ConstArg { + pub fn source(&self, db: &dyn DefDatabase) -> ast::ConstArg { let src = self.lookup(db).id; let file_id = src.file_id; let root = &db.parse_or_expand(file_id); @@ -742,7 +742,7 @@ pub enum GeneralConstId { impl_from!(ConstId, ConstBlockId, InTypeConstId for GeneralConstId); impl GeneralConstId { - pub fn generic_def(self, db: &dyn db::DefDatabase) -> Option { + pub fn generic_def(self, db: &dyn DefDatabase) -> Option { match self { GeneralConstId::ConstId(it) => Some(it.into()), GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(), @@ -750,7 +750,7 @@ impl GeneralConstId { } } - pub fn name(self, db: &dyn db::DefDatabase) -> String { + pub fn name(self, db: &dyn DefDatabase) -> String { match self { GeneralConstId::ConstId(const_id) => db .const_data(const_id) @@ -933,7 +933,7 @@ pub enum VariantId { impl_from!(EnumVariantId, StructId, UnionId for VariantId); impl VariantId { - pub fn variant_data(self, db: &dyn db::DefDatabase) -> Arc { + pub fn variant_data(self, db: &dyn DefDatabase) -> Arc { match self { VariantId::StructId(it) => db.struct_data(it).variant_data.clone(), VariantId::UnionId(it) => db.union_data(it).variant_data.clone(), @@ -943,7 +943,7 @@ impl VariantId { } } - pub fn file_id(self, db: &dyn db::DefDatabase) -> HirFileId { + pub fn file_id(self, db: &dyn DefDatabase) -> HirFileId { match self { VariantId::EnumVariantId(it) => it.parent.lookup(db).id.file_id(), VariantId::StructId(it) => it.lookup(db).id.file_id(), @@ -960,22 +960,12 @@ impl VariantId { } } -trait Intern { - type ID; - fn intern(self, db: &dyn db::DefDatabase) -> Self::ID; -} - -pub trait Lookup { - type Data; - fn lookup(&self, db: &dyn db::DefDatabase) -> Self::Data; -} - pub trait HasModule { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId; + fn module(&self, db: &dyn DefDatabase) -> ModuleId; } impl HasModule for ItemContainerId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { match *self { ItemContainerId::ModuleId(it) => it, ItemContainerId::ImplId(it) => it.lookup(db).container, @@ -986,13 +976,13 @@ impl HasModule for ItemContainerId { } impl HasModule for AssocItemLoc { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { self.container.module(db) } } impl HasModule for AdtId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { AdtId::StructId(it) => it.lookup(db).container, AdtId::UnionId(it) => it.lookup(db).container, @@ -1002,13 +992,13 @@ impl HasModule for AdtId { } impl HasModule for ExternCrateId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { self.lookup(db).container } } impl HasModule for VariantId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { VariantId::EnumVariantId(it) => it.parent.lookup(db).container, VariantId::StructId(it) => it.lookup(db).container, @@ -1018,7 +1008,7 @@ impl HasModule for VariantId { } impl HasModule for MacroId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { MacroId::MacroRulesId(it) => it.lookup(db).container, MacroId::Macro2Id(it) => it.lookup(db).container, @@ -1028,7 +1018,7 @@ impl HasModule for MacroId { } impl HasModule for TypeOwnerId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { TypeOwnerId::FunctionId(it) => it.lookup(db).module(db), TypeOwnerId::StaticId(it) => it.lookup(db).module(db), @@ -1045,7 +1035,7 @@ impl HasModule for TypeOwnerId { } impl HasModule for DefWithBodyId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { DefWithBodyId::FunctionId(it) => it.lookup(db).module(db), DefWithBodyId::StaticId(it) => it.lookup(db).module(db), @@ -1057,7 +1047,7 @@ impl HasModule for DefWithBodyId { } impl HasModule for GenericDefId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { GenericDefId::FunctionId(it) => it.lookup(db).module(db), GenericDefId::AdtId(it) => it.module(db), @@ -1072,13 +1062,13 @@ impl HasModule for GenericDefId { } impl HasModule for TypeAliasId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { self.lookup(db).module(db) } } impl HasModule for TraitId { - fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { self.lookup(db).container } } @@ -1087,7 +1077,7 @@ impl ModuleDefId { /// Returns the module containing `self` (or `self`, if `self` is itself a module). /// /// Returns `None` if `self` refers to a primitive type. - pub fn module(&self, db: &dyn db::DefDatabase) -> Option { + pub fn module(&self, db: &dyn DefDatabase) -> Option { Some(match self { ModuleDefId::ModuleId(id) => *id, ModuleDefId::FunctionId(id) => id.lookup(db).module(db), @@ -1105,7 +1095,7 @@ impl ModuleDefId { } impl AttrDefId { - pub fn krate(&self, db: &dyn db::DefDatabase) -> CrateId { + pub fn krate(&self, db: &dyn DefDatabase) -> CrateId { match self { AttrDefId::ModuleId(it) => it.krate, AttrDefId::FieldId(it) => it.parent.module(db).krate, @@ -1171,7 +1161,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> { return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation"))); }; - let call_site = span_map.span_for_range(self.value.syntax().text_range()).ctx; + let call_site = span_map.span_for_range(self.value.syntax().text_range()); macro_call_as_call_id_with_eager( db, @@ -1201,7 +1191,7 @@ impl AstIdWithPath { fn macro_call_as_call_id( db: &dyn ExpandDatabase, call: &AstIdWithPath, - call_site: SyntaxContextId, + call_site: Span, expand_to: ExpandTo, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option + Copy, @@ -1213,7 +1203,7 @@ fn macro_call_as_call_id( fn macro_call_as_call_id_with_eager( db: &dyn ExpandDatabase, call: &AstIdWithPath, - call_site: SyntaxContextId, + call_site: Span, expand_to: ExpandTo, krate: CrateId, resolver: impl FnOnce(path::ModPath) -> Option, @@ -1243,83 +1233,12 @@ fn macro_call_as_call_id_with_eager( Ok(res) } -pub fn macro_id_to_def_id(db: &dyn db::DefDatabase, id: MacroId) -> MacroDefId { - match id { - MacroId::Macro2Id(it) => { - let loc = it.lookup(db); - - let item_tree = loc.id.item_tree(db); - let makro = &item_tree[loc.id.value]; - let in_file = |m: FileAstId| InFile::new(loc.id.file_id(), m.upcast()); - MacroDefId { - krate: loc.container.krate, - kind: match loc.expander { - MacroExpander::Declarative => MacroDefKind::Declarative(in_file(makro.ast_id)), - MacroExpander::BuiltIn(it) => MacroDefKind::BuiltIn(it, in_file(makro.ast_id)), - MacroExpander::BuiltInAttr(it) => { - MacroDefKind::BuiltInAttr(it, in_file(makro.ast_id)) - } - MacroExpander::BuiltInDerive(it) => { - MacroDefKind::BuiltInDerive(it, in_file(makro.ast_id)) - } - MacroExpander::BuiltInEager(it) => { - MacroDefKind::BuiltInEager(it, in_file(makro.ast_id)) - } - }, - local_inner: false, - allow_internal_unsafe: loc.allow_internal_unsafe, - } - } - MacroId::MacroRulesId(it) => { - let loc = it.lookup(db); - - let item_tree = loc.id.item_tree(db); - let makro = &item_tree[loc.id.value]; - let in_file = |m: FileAstId| InFile::new(loc.id.file_id(), m.upcast()); - MacroDefId { - krate: loc.container.krate, - kind: match loc.expander { - MacroExpander::Declarative => MacroDefKind::Declarative(in_file(makro.ast_id)), - MacroExpander::BuiltIn(it) => MacroDefKind::BuiltIn(it, in_file(makro.ast_id)), - MacroExpander::BuiltInAttr(it) => { - MacroDefKind::BuiltInAttr(it, in_file(makro.ast_id)) - } - MacroExpander::BuiltInDerive(it) => { - MacroDefKind::BuiltInDerive(it, in_file(makro.ast_id)) - } - MacroExpander::BuiltInEager(it) => { - MacroDefKind::BuiltInEager(it, in_file(makro.ast_id)) - } - }, - local_inner: loc.local_inner, - allow_internal_unsafe: loc.allow_internal_unsafe, - } - } - MacroId::ProcMacroId(it) => { - let loc = it.lookup(db); - - let item_tree = loc.id.item_tree(db); - let makro = &item_tree[loc.id.value]; - MacroDefId { - krate: loc.container.krate, - kind: MacroDefKind::ProcMacro( - loc.expander, - loc.kind, - InFile::new(loc.id.file_id(), makro.ast_id), - ), - local_inner: false, - allow_internal_unsafe: false, - } - } - } -} - fn derive_macro_as_call_id( - db: &dyn db::DefDatabase, + db: &dyn DefDatabase, item_attr: &AstIdWithPath, derive_attr_index: AttrId, derive_pos: u32, - call_site: SyntaxContextId, + call_site: Span, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { @@ -1340,7 +1259,7 @@ fn derive_macro_as_call_id( } fn attr_macro_as_call_id( - db: &dyn db::DefDatabase, + db: &dyn DefDatabase, item_attr: &AstIdWithPath, macro_attr: &Attr, krate: CrateId, @@ -1349,7 +1268,7 @@ fn attr_macro_as_call_id( let arg = match macro_attr.input.as_deref() { Some(AttrInput::TokenTree(tt)) => { let mut tt = tt.as_ref().clone(); - tt.delimiter = tt::Delimiter::DUMMY_INVISIBLE; + tt.delimiter = tt::Delimiter::invisible_spanned(macro_attr.span); Some(tt) } @@ -1364,7 +1283,7 @@ fn attr_macro_as_call_id( attr_args: arg.map(Arc::new), invoc_attr_index: macro_attr.id, }, - macro_attr.ctxt, + macro_attr.span, ) } diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs index a3505b65fe72..395b69d284f5 100644 --- a/crates/hir-def/src/lower.rs +++ b/crates/hir-def/src/lower.rs @@ -3,7 +3,7 @@ use std::cell::OnceCell; use hir_expand::{ ast_id_map::{AstIdMap, AstIdNode}, - span::{SpanMap, SpanMapRef}, + span_map::{SpanMap, SpanMapRef}, AstId, HirFileId, InFile, }; use syntax::ast; diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 514219ee7150..d4798f4507d1 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -163,31 +163,43 @@ fn main() { ""; } fn test_assert_expand() { check( r#" -#[rustc_builtin_macro] -macro_rules! assert { - ($cond:expr) => ({ /* compiler built-in */ }); - ($cond:expr, $($args:tt)*) => ({ /* compiler built-in */ }) -} - +//- minicore: assert fn main() { assert!(true, "{} {:?}", arg1(a, b, c), arg2); } "#, - expect![[r##" -#[rustc_builtin_macro] -macro_rules! assert { - ($cond:expr) => ({ /* compiler built-in */ }); - ($cond:expr, $($args:tt)*) => ({ /* compiler built-in */ }) -} - + expect![[r#" fn main() { { if !(true ) { - $crate::panic!("{} {:?}", arg1(a, b, c), arg2); + $crate::panic::panic_2021!("{} {:?}", arg1(a, b, c), arg2); } }; } -"##]], +"#]], + ); +} + +// FIXME: This is the wrong expansion, see FIXME on `builtin_fn_macro::use_panic_2021` +#[test] +fn test_assert_expand_2015() { + check( + r#" +//- minicore: assert +//- /main.rs edition:2015 +fn main() { + assert!(true, "{} {:?}", arg1(a, b, c), arg2); +} +"#, + expect![[r#" +fn main() { + { + if !(true ) { + $crate::panic::panic_2021!("{} {:?}", arg1(a, b, c), arg2); + } + }; +} +"#]], ); } diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index 9bf2a50d57c9..f2046bfbce4d 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1218,8 +1218,10 @@ m! { macro_rules! m { ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} ) } -#[doc = " Single Line Doc 1"] -#[doc = "\n MultiLines Doc\n "] fn bar() {} +#[doc = r" Single Line Doc 1"] +#[doc = r" + MultiLines Doc + "] fn bar() {} "##]], ); } @@ -1260,8 +1262,10 @@ m! { macro_rules! m { ($(#[$ m:meta])+) => ( $(#[$m])+ fn bar() {} ) } -#[doc = " 錦瑟無端五十弦,一弦一柱思華年。"] -#[doc = "\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\n "] fn bar() {} +#[doc = r" 錦瑟無端五十弦,一弦一柱思華年。"] +#[doc = r" + 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 + "] fn bar() {} "##]], ); } @@ -1281,7 +1285,7 @@ m! { macro_rules! m { ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} ) } -#[doc = " \\ \" \'"] fn bar() {} +#[doc = r#" \ " '"#] fn bar() {} "##]], ); } diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs index 7e7b40044218..e875950e4e5f 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs @@ -18,7 +18,7 @@ macro_rules! m { ($($false:ident)*) => ($false); (double_dollar) => ($$); ($) => (m!($);); - ($($t:tt)*) => ($( ${ignore(t)} ${index()} )-*); + ($($t:tt)*) => ($( ${ignore($t)} ${index()} )-*); } m!($); "#, @@ -33,7 +33,7 @@ macro_rules! m { ($($false:ident)*) => ($false); (double_dollar) => ($$); ($) => (m!($);); - ($($t:tt)*) => ($( ${ignore(t)} ${index()} )-*); + ($($t:tt)*) => ($( ${ignore($t)} ${index()} )-*); } m!($); "#]], diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs index 967b5ad36bab..6560d0ec4664 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs @@ -77,13 +77,13 @@ fn test_metavar_exprs() { check( r#" macro_rules! m { - ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); + ( $( $t:tt )* ) => ( $( ${ignore($t)} -${index()} )-* ); } const _: i32 = m!(a b c); "#, expect![[r#" macro_rules! m { - ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); + ( $( $t:tt )* ) => ( $( ${ignore($t)} -${index()} )-* ); } const _: i32 = -0--1--2; "#]], @@ -96,7 +96,7 @@ fn count_basic() { r#" macro_rules! m { ($($t:ident),*) => { - ${count(t)} + ${count($t)} } } @@ -109,7 +109,7 @@ fn test() { expect![[r#" macro_rules! m { ($($t:ident),*) => { - ${count(t)} + ${count($t)} } } @@ -130,9 +130,9 @@ macro_rules! foo { ($( $( $($t:ident)* ),* );*) => { $( { - let depth_none = ${count(t)}; - let depth_zero = ${count(t, 0)}; - let depth_one = ${count(t, 1)}; + let depth_none = ${count($t)}; + let depth_zero = ${count($t, 0)}; + let depth_one = ${count($t, 1)}; } )* } @@ -150,9 +150,9 @@ macro_rules! foo { ($( $( $($t:ident)* ),* );*) => { $( { - let depth_none = ${count(t)}; - let depth_zero = ${count(t, 0)}; - let depth_one = ${count(t, 1)}; + let depth_none = ${count($t)}; + let depth_zero = ${count($t, 0)}; + let depth_one = ${count($t, 1)}; } )* } @@ -160,11 +160,11 @@ macro_rules! foo { fn bar() { { - let depth_none = 6; + let depth_none = 3; let depth_zero = 3; let depth_one = 6; } { - let depth_none = 3; + let depth_none = 1; let depth_zero = 1; let depth_one = 3; } @@ -178,12 +178,12 @@ fn count_depth_out_of_bounds() { check( r#" macro_rules! foo { - ($($t:ident)*) => { ${count(t, 1)} }; - ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* } + ($($t:ident)*) => { ${count($t, 1)} }; + ($( $( $l:literal )* );*) => { $(${count($l, 1)};)* } } macro_rules! bar { - ($($t:ident)*) => { ${count(t, 1024)} }; - ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* } + ($($t:ident)*) => { ${count($t, 1024)} }; + ($( $( $l:literal )* );*) => { $(${count($l, 8192)};)* } } fn test() { @@ -195,19 +195,21 @@ fn test() { "#, expect![[r#" macro_rules! foo { - ($($t:ident)*) => { ${count(t, 1)} }; - ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* } + ($($t:ident)*) => { ${count($t, 1)} }; + ($( $( $l:literal )* );*) => { $(${count($l, 1)};)* } } macro_rules! bar { - ($($t:ident)*) => { ${count(t, 1024)} }; - ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* } + ($($t:ident)*) => { ${count($t, 1024)} }; + ($( $( $l:literal )* );*) => { $(${count($l, 8192)};)* } } fn test() { - /* error: ${count} out of bounds */; - /* error: ${count} out of bounds */; - /* error: ${count} out of bounds */; - /* error: ${count} out of bounds */; + 2; + 2; + 1;; + 2; + 2; + 1;; } "#]], ); @@ -218,8 +220,8 @@ fn misplaced_count() { check( r#" macro_rules! foo { - ($($t:ident)*) => { $(${count(t)})* }; - ($l:literal) => { ${count(l)} } + ($($t:ident)*) => { $(${count($t)})* }; + ($l:literal) => { ${count($l)} } } fn test() { @@ -229,13 +231,13 @@ fn test() { "#, expect![[r#" macro_rules! foo { - ($($t:ident)*) => { $(${count(t)})* }; - ($l:literal) => { ${count(l)} } + ($($t:ident)*) => { $(${count($t)})* }; + ($l:literal) => { ${count($l)} } } fn test() { - /* error: ${count} misplaced */; - /* error: ${count} misplaced */; + 1 1 1; + 1; } "#]], ); @@ -246,13 +248,13 @@ fn malformed_count() { check( r#" macro_rules! too_many_args { - ($($t:ident)*) => { ${count(t, 1, leftover)} } + ($($t:ident)*) => { ${count($t, 1, leftover)} } } macro_rules! depth_suffixed { - ($($t:ident)*) => { ${count(t, 0usize)} } + ($($t:ident)*) => { ${count($t, 0usize)} } } macro_rules! depth_too_large { - ($($t:ident)*) => { ${count(t, 18446744073709551616)} } + ($($t:ident)*) => { ${count($t, 18446744073709551616)} } } fn test() { @@ -263,13 +265,13 @@ fn test() { "#, expect![[r#" macro_rules! too_many_args { - ($($t:ident)*) => { ${count(t, 1, leftover)} } + ($($t:ident)*) => { ${count($t, 1, leftover)} } } macro_rules! depth_suffixed { - ($($t:ident)*) => { ${count(t, 0usize)} } + ($($t:ident)*) => { ${count($t, 0usize)} } } macro_rules! depth_too_large { - ($($t:ident)*) => { ${count(t, 18446744073709551616)} } + ($($t:ident)*) => { ${count($t, 18446744073709551616)} } } fn test() { @@ -288,7 +290,7 @@ fn count_interaction_with_empty_binding() { r#" macro_rules! m { ($($t:ident),*) => { - ${count(t, 100)} + ${count($t, 100)} } } @@ -299,7 +301,7 @@ fn test() { expect![[r#" macro_rules! m { ($($t:ident),*) => { - ${count(t, 100)} + ${count($t, 100)} } } diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index be2a503d82b1..ee806361237a 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -16,9 +16,15 @@ mod proc_macros; use std::{iter, ops::Range, sync}; -use base_db::{fixture::WithFixture, span::SpanData, ProcMacro, SourceDatabase}; +use base_db::SourceDatabase; use expect_test::Expect; -use hir_expand::{db::ExpandDatabase, span::SpanMapRef, InFile, MacroFileId, MacroFileIdExt}; +use hir_expand::{ + db::ExpandDatabase, + proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind}, + span_map::SpanMapRef, + InFile, MacroFileId, MacroFileIdExt, +}; +use span::Span; use stdx::format_to; use syntax::{ ast::{self, edit::IndentLevel}, @@ -26,10 +32,10 @@ use syntax::{ SyntaxKind::{COMMENT, EOF, IDENT, LIFETIME_IDENT}, SyntaxNode, T, }; +use test_fixture::WithFixture; use crate::{ db::DefDatabase, - macro_id_to_def_id, nameres::{DefMap, MacroSubNs, ModuleSource}, resolver::HasResolver, src::HasSource, @@ -50,7 +56,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream .into(), ProcMacro { name: "identity_when_valid".into(), - kind: base_db::ProcMacroKind::Attr, + kind: ProcMacroKind::Attr, expander: sync::Arc::new(IdentityWhenValidProcMacroExpander), }, )]; @@ -90,7 +96,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream .as_call_id_with_errors(&db, krate, |path| { resolver .resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang)) - .map(|(it, _)| macro_id_to_def_id(&db, it)) + .map(|(it, _)| db.macro_def(it)) }) .unwrap(); let macro_call_id = res.value.unwrap(); @@ -307,16 +313,16 @@ fn pretty_print_macro_expansion( // compile errors. #[derive(Debug)] struct IdentityWhenValidProcMacroExpander; -impl base_db::ProcMacroExpander for IdentityWhenValidProcMacroExpander { +impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { fn expand( &self, subtree: &Subtree, _: Option<&Subtree>, _: &base_db::Env, - _: SpanData, - _: SpanData, - _: SpanData, - ) -> Result { + _: Span, + _: Span, + _: Span, + ) -> Result { let (parse, _) = ::mbe::token_tree_to_syntax_node(subtree, ::mbe::TopEntryPoint::MacroItems); if parse.errors().is_empty() { diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 9a9fa0e02b08..52a981fd19eb 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -59,8 +59,11 @@ mod tests; use std::{cmp::Ord, ops::Deref}; -use base_db::{CrateId, Edition, FileId, ProcMacroKind}; -use hir_expand::{ast_id_map::FileAstId, name::Name, HirFileId, InFile, MacroCallId, MacroDefId}; +use base_db::{CrateId, Edition, FileId}; +use hir_expand::{ + ast_id_map::FileAstId, name::Name, proc_macro::ProcMacroKind, HirFileId, InFile, MacroCallId, + MacroDefId, +}; use itertools::Itertools; use la_arena::Arena; use profile::Count; @@ -97,7 +100,7 @@ pub struct DefMap { /// contains this block. block: Option, /// The modules and their data declared in this crate. - modules: Arena, + pub modules: Arena, krate: CrateId, /// The prelude module for this crate. This either comes from an import /// marked with the `prelude_import` attribute, or (in the normal case) from @@ -623,8 +626,9 @@ impl DefMap { self.diagnostics.as_slice() } - pub fn recursion_limit(&self) -> Option { - self.data.recursion_limit + pub fn recursion_limit(&self) -> u32 { + // 128 is the default in rustc + self.data.recursion_limit.unwrap_or(128) } } diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs index a7abf445918a..6288b8366bf0 100644 --- a/crates/hir-def/src/nameres/attr_resolution.rs +++ b/crates/hir-def/src/nameres/attr_resolution.rs @@ -8,7 +8,6 @@ use crate::{ attr_macro_as_call_id, db::DefDatabase, item_scope::BuiltinShadowMode, - macro_id_to_def_id, nameres::path_resolution::ResolveMode, path::{ModPath, PathKind}, AstIdWithPath, LocalModuleId, UnresolvedMacro, @@ -63,7 +62,7 @@ impl DefMap { &ast_id, attr, self.krate, - macro_id_to_def_id(db, def), + db.macro_def(def), ))) } diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index b3a10a3869a4..3763bfcbcfaf 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -5,7 +5,7 @@ use std::{cmp::Ordering, iter, mem}; -use base_db::{span::SyntaxContextId, CrateId, Dependency, Edition, FileId}; +use base_db::{CrateId, Dependency, Edition, FileId}; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ @@ -15,7 +15,7 @@ use hir_expand::{ builtin_derive_macro::find_builtin_derive, builtin_fn_macro::find_builtin_macro, name::{name, AsName, Name}, - proc_macro::ProcMacroExpander, + proc_macro::CustomProcMacroExpander, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, }; @@ -23,6 +23,7 @@ use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; +use span::{Span, SyntaxContextId}; use stdx::always; use syntax::{ast, SmolStr}; use triomphe::Arc; @@ -35,9 +36,9 @@ use crate::{ item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, item_tree::{ self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, - MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId, + Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, }, - macro_call_as_call_id, macro_call_as_call_id_with_eager, macro_id_to_def_id, + macro_call_as_call_id, macro_call_as_call_id_with_eager, nameres::{ diagnostics::DefDiagnostic, mod_resolution::ModDir, @@ -53,8 +54,9 @@ use crate::{ AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, - MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, - StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, + MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, + ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, + UnresolvedMacro, UseId, UseLoc, }; static GLOB_RECURSION_LIMIT: Limit = Limit::new(100); @@ -86,16 +88,21 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI // FIXME: a hacky way to create a Name from string. let name = tt::Ident { text: it.name.clone(), - span: tt::SpanData { + span: Span { range: syntax::TextRange::empty(syntax::TextSize::new(0)), - anchor: base_db::span::SpanAnchor { + anchor: span::SpanAnchor { file_id: FileId::BOGUS, - ast_id: base_db::span::ROOT_ERASED_FILE_AST_ID, + ast_id: span::ROOT_ERASED_FILE_AST_ID, }, ctx: SyntaxContextId::ROOT, }, }; - (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32))) + ( + name.as_name(), + CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId( + idx as u32, + )), + ) }) .collect()) } @@ -222,13 +229,13 @@ enum MacroDirectiveKind { FnLike { ast_id: AstIdWithPath, expand_to: ExpandTo, - call_site: SyntaxContextId, + call_site: Span, }, Derive { ast_id: AstIdWithPath, derive_attr: AttrId, derive_pos: usize, - call_site: SyntaxContextId, + call_site: Span, }, Attr { ast_id: AstIdWithPath, @@ -253,7 +260,7 @@ struct DefCollector<'a> { /// built by the build system, and is the list of proc. macros we can actually expand. It is /// empty when proc. macro support is disabled (in which case we still do name resolution for /// them). - proc_macros: Result, Box>, + proc_macros: Result, Box>, is_proc_macro: bool, from_glob_import: PerNsGlobImports, /// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute. @@ -545,6 +552,8 @@ impl DefCollector<'_> { Edition::Edition2015 => name![rust_2015], Edition::Edition2018 => name![rust_2018], Edition::Edition2021 => name![rust_2021], + // FIXME: update this when rust_2024 exists + Edition::Edition2024 => name![rust_2021], }; let path_kind = match self.def_map.data.edition { @@ -603,18 +612,21 @@ impl DefCollector<'_> { let (expander, kind) = match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) { Ok(Some(&(_, expander))) => (expander, kind), - _ => (ProcMacroExpander::dummy(), kind), + _ => (CustomProcMacroExpander::dummy(), kind), }; - let proc_macro_id = - ProcMacroLoc { container: self.def_map.crate_root(), id, expander, kind } - .intern(self.db); + let proc_macro_id = ProcMacroLoc { + container: self.def_map.crate_root(), + id, + expander, + kind, + edition: self.def_map.data.edition, + } + .intern(self.db); self.define_proc_macro(def.name.clone(), proc_macro_id); let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); if let ProcMacroKind::CustomDerive { helpers } = def.kind { - crate_data - .exported_derives - .insert(macro_id_to_def_id(self.db, proc_macro_id.into()), helpers); + crate_data.exported_derives.insert(self.db.macro_def(proc_macro_id.into()), helpers); } crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); } @@ -1125,10 +1137,7 @@ impl DefCollector<'_> { BuiltinShadowMode::Module, Some(subns), ); - resolved_res - .resolved_def - .take_macros() - .map(|it| (it, macro_id_to_def_id(self.db, it))) + resolved_res.resolved_def.take_macros().map(|it| (it, self.db.macro_def(it))) }; let resolver_def_id = |path| resolver(path).map(|(_, it)| it); @@ -1143,6 +1152,9 @@ impl DefCollector<'_> { resolver_def_id, ); if let Ok(Some(call_id)) = call_id { + self.def_map.modules[directive.module_id] + .scope + .add_macro_invoc(ast_id.ast_id, call_id); push_resolved(directive, call_id); res = ReachedFixedPoint::No; @@ -1299,14 +1311,13 @@ impl DefCollector<'_> { // Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute. let call_id = attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def); - let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id); // If proc attribute macro expansion is disabled, skip expanding it here if !self.db.expand_proc_attr_macros() { self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro( directive.module_id, - loc.kind, - loc.def.krate, + self.db.lookup_intern_macro_call(call_id).kind, + def.krate, )); return recollect_without(self); } @@ -1314,14 +1325,14 @@ impl DefCollector<'_> { // Skip #[test]/#[bench] expansion, which would merely result in more memory usage // due to duplicating functions into macro expansions if matches!( - loc.def.kind, + def.kind, MacroDefKind::BuiltInAttr(expander, _) if expander.is_test() || expander.is_bench() ) { return recollect_without(self); } - if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind { + if let MacroDefKind::ProcMacro(exp, ..) = def.kind { if exp.is_dummy() { // If there's no expander for the proc macro (e.g. // because proc macros are disabled, or building the @@ -1329,8 +1340,8 @@ impl DefCollector<'_> { // expansion like we would if it was disabled self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro( directive.module_id, - loc.kind, - loc.def.krate, + self.db.lookup_intern_macro_call(call_id).kind, + def.krate, )); return recollect_without(self); @@ -1436,10 +1447,7 @@ impl DefCollector<'_> { BuiltinShadowMode::Module, Some(MacroSubNs::Bang), ); - resolved_res - .resolved_def - .take_macros() - .map(|it| macro_id_to_def_id(self.db, it)) + resolved_res.resolved_def.take_macros().map(|it| self.db.macro_def(it)) }, ); if let Err(UnresolvedMacro { path }) = macro_call_as_call_id { @@ -1645,7 +1653,7 @@ impl ModCollector<'_, '_> { ), ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac], container), ModItem::MacroRules(id) => self.collect_macro_rules(id, module), - ModItem::MacroDef(id) => self.collect_macro_def(id, module), + ModItem::Macro2(id) => self.collect_macro_def(id, module), ModItem::Impl(imp) => { let impl_id = ImplLoc { container: module, id: ItemTreeId::new(self.tree_id, imp) } @@ -2090,11 +2098,11 @@ impl ModCollector<'_, '_> { // FIXME: a hacky way to create a Name from string. name = tt::Ident { text: it.clone(), - span: tt::SpanData { + span: Span { range: syntax::TextRange::empty(syntax::TextSize::new(0)), - anchor: base_db::span::SpanAnchor { + anchor: span::SpanAnchor { file_id: FileId::BOGUS, - ast_id: base_db::span::ROOT_ERASED_FILE_AST_ID, + ast_id: span::ROOT_ERASED_FILE_AST_ID, }, ctx: SyntaxContextId::ROOT, }, @@ -2136,12 +2144,16 @@ impl ModCollector<'_, '_> { }; let allow_internal_unsafe = attrs.by_key("allow_internal_unsafe").exists(); + let mut flags = MacroRulesLocFlags::empty(); + flags.set(MacroRulesLocFlags::LOCAL_INNER, local_inner); + flags.set(MacroRulesLocFlags::ALLOW_INTERNAL_UNSAFE, allow_internal_unsafe); + let macro_id = MacroRulesLoc { container: module, id: ItemTreeId::new(self.tree_id, id), - local_inner, - allow_internal_unsafe, + flags, expander, + edition: self.def_collector.def_map.data.edition, } .intern(self.def_collector.db); self.def_collector.define_macro_rules( @@ -2152,7 +2164,7 @@ impl ModCollector<'_, '_> { ); } - fn collect_macro_def(&mut self, id: FileItemTreeId, module: ModuleId) { + fn collect_macro_def(&mut self, id: FileItemTreeId, module: ModuleId) { let krate = self.def_collector.def_map.krate; let mac = &self.item_tree[id]; let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast()); @@ -2207,6 +2219,7 @@ impl ModCollector<'_, '_> { id: ItemTreeId::new(self.tree_id, id), expander, allow_internal_unsafe, + edition: self.def_collector.def_map.data.edition, } .intern(self.def_collector.db); self.def_collector.define_macro_def( @@ -2220,7 +2233,7 @@ impl ModCollector<'_, '_> { Arc::get_mut(&mut self.def_collector.def_map.data) .unwrap() .exported_derives - .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers); + .insert(self.def_collector.db.macro_def(macro_id.into()), helpers); } } } @@ -2259,7 +2272,7 @@ impl ModCollector<'_, '_> { Some(MacroSubNs::Bang), ) }) - .map(|it| macro_id_to_def_id(self.def_collector.db, it)) + .map(|it| self.def_collector.db.macro_def(it)) }) }, |path| { @@ -2271,7 +2284,7 @@ impl ModCollector<'_, '_> { BuiltinShadowMode::Module, Some(MacroSubNs::Bang), ); - resolved_res.resolved_def.take_macros().map(|it| macro_id_to_def_id(db, it)) + resolved_res.resolved_def.take_macros().map(|it| db.macro_def(it)) }, ) { // FIXME: if there were errors, this mightve been in the eager expansion from an @@ -2279,10 +2292,13 @@ impl ModCollector<'_, '_> { if res.err.is_none() { // Legacy macros need to be expanded immediately, so that any macros they produce // are in scope. - if let Some(val) = res.value { + if let Some(call_id) = res.value { + self.def_collector.def_map.modules[self.module_id] + .scope + .add_macro_invoc(ast_id.ast_id, call_id); self.def_collector.collect_macro_expansion( self.module_id, - val, + call_id, self.macro_depth + 1, container, ); @@ -2296,7 +2312,7 @@ impl ModCollector<'_, '_> { self.def_collector.unresolved_macros.push(MacroDirective { module_id: self.module_id, depth: self.macro_depth + 1, - kind: MacroDirectiveKind::FnLike { ast_id, expand_to: expand_to, call_site }, + kind: MacroDirectiveKind::FnLike { ast_id, expand_to, call_site }, container, }); } @@ -2363,8 +2379,10 @@ impl ModCollector<'_, '_> { #[cfg(test)] mod tests { + use base_db::SourceDatabase; + use test_fixture::WithFixture; + use crate::{db::DefDatabase, test_db::TestDB}; - use base_db::{fixture::WithFixture, SourceDatabase}; use super::*; diff --git a/crates/hir-def/src/nameres/proc_macro.rs b/crates/hir-def/src/nameres/proc_macro.rs index 751b7beaac15..c126fdac1c62 100644 --- a/crates/hir-def/src/nameres/proc_macro.rs +++ b/crates/hir-def/src/nameres/proc_macro.rs @@ -19,11 +19,13 @@ pub enum ProcMacroKind { } impl ProcMacroKind { - pub(super) fn to_basedb_kind(&self) -> base_db::ProcMacroKind { + pub(super) fn to_basedb_kind(&self) -> hir_expand::proc_macro::ProcMacroKind { match self { - ProcMacroKind::CustomDerive { .. } => base_db::ProcMacroKind::CustomDerive, - ProcMacroKind::FnLike => base_db::ProcMacroKind::FuncLike, - ProcMacroKind::Attr => base_db::ProcMacroKind::Attr, + ProcMacroKind::CustomDerive { .. } => { + hir_expand::proc_macro::ProcMacroKind::CustomDerive + } + ProcMacroKind::FnLike => hir_expand::proc_macro::ProcMacroKind::FuncLike, + ProcMacroKind::Attr => hir_expand::proc_macro::ProcMacroKind::Attr, } } } diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs index b2ffbbe4c5d8..17e82dc16c42 100644 --- a/crates/hir-def/src/nameres/tests.rs +++ b/crates/hir-def/src/nameres/tests.rs @@ -4,8 +4,9 @@ mod macros; mod mod_resolution; mod primitives; -use base_db::{fixture::WithFixture, SourceDatabase}; +use base_db::SourceDatabase; use expect_test::{expect, Expect}; +use test_fixture::WithFixture; use triomphe::Arc; use crate::{db::DefDatabase, nameres::DefMap, test_db::TestDB}; diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs index 78cb78e833ec..6efced02718a 100644 --- a/crates/hir-def/src/nameres/tests/incremental.rs +++ b/crates/hir-def/src/nameres/tests/incremental.rs @@ -1,11 +1,8 @@ use base_db::{SourceDatabase, SourceDatabaseExt}; +use test_fixture::WithFixture; use triomphe::Arc; -use crate::{ - db::DefDatabase, - nameres::tests::{TestDB, WithFixture}, - AdtId, ModuleDefId, -}; +use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId}; fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: &str) { let (mut db, pos) = TestDB::with_position(ra_fixture_initial); diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index e64fa0b46f13..48fe43450a71 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -1,6 +1,12 @@ -use super::*; +use expect_test::expect; +use test_fixture::WithFixture; + use itertools::Itertools; +use crate::nameres::tests::check; + +use super::*; + #[test] fn macro_rules_are_globally_visible() { check( diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 2ac1516ec07b..301391516d64 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -2,7 +2,10 @@ use std::{fmt, hash::BuildHasherDefault}; use base_db::CrateId; -use hir_expand::name::{name, Name}; +use hir_expand::{ + name::{name, Name}, + MacroDefId, +}; use indexmap::IndexMap; use intern::Interned; use rustc_hash::FxHashSet; @@ -406,6 +409,15 @@ impl Resolver { .take_macros_import() } + pub fn resolve_path_as_macro_def( + &self, + db: &dyn DefDatabase, + path: &ModPath, + expected_macro_kind: Option, + ) -> Option { + self.resolve_path_as_macro(db, path, expected_macro_kind).map(|(it, _)| db.macro_def(it)) + } + /// Returns a set of names available in the current scope. /// /// Note that this is a somewhat fuzzy concept -- internally, the compiler diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs index f5803653c73b..49688c5ee9c2 100644 --- a/crates/hir-def/src/visibility.rs +++ b/crates/hir-def/src/visibility.rs @@ -2,7 +2,7 @@ use std::iter; -use hir_expand::{span::SpanMapRef, InFile}; +use hir_expand::{span_map::SpanMapRef, InFile}; use la_arena::ArenaMap; use syntax::ast; use triomphe::Arc; diff --git a/crates/hir-expand/Cargo.toml b/crates/hir-expand/Cargo.toml index 361bbec4318f..506a188a211d 100644 --- a/crates/hir-expand/Cargo.toml +++ b/crates/hir-expand/Cargo.toml @@ -15,7 +15,7 @@ doctest = false cov-mark = "2.0.0-pre.1" tracing.workspace = true either.workspace = true -rustc-hash = "1.1.0" +rustc-hash.workspace = true la-arena.workspace = true itertools.workspace = true hashbrown.workspace = true @@ -32,6 +32,10 @@ profile.workspace = true tt.workspace = true mbe.workspace = true limit.workspace = true +span.workspace = true [dev-dependencies] expect-test = "1.4.0" + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/hir-expand/src/ast_id_map.rs b/crates/hir-expand/src/ast_id_map.rs index be0b72f9dfa4..d0d229e1319f 100644 --- a/crates/hir-expand/src/ast_id_map.rs +++ b/crates/hir-expand/src/ast_id_map.rs @@ -5,6 +5,8 @@ //! item as an ID. That way, id's don't change unless the set of items itself //! changes. +// FIXME: Consider moving this into the span crate + use std::{ any::type_name, fmt, @@ -17,9 +19,9 @@ use profile::Count; use rustc_hash::FxHasher; use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; -use crate::db; +use crate::db::ExpandDatabase; -pub use base_db::span::ErasedFileAstId; +pub use span::ErasedFileAstId; /// `AstId` points to an AST node in any file. /// @@ -27,13 +29,13 @@ pub use base_db::span::ErasedFileAstId; pub type AstId = crate::InFile>; impl AstId { - pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N { + pub fn to_node(&self, db: &dyn ExpandDatabase) -> N { self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) } - pub fn to_in_file_node(&self, db: &dyn db::ExpandDatabase) -> crate::InFile { + pub fn to_in_file_node(&self, db: &dyn ExpandDatabase) -> crate::InFile { crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))) } - pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> AstPtr { + pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> AstPtr { db.ast_id_map(self.file_id).get(self.value) } } @@ -41,7 +43,7 @@ impl AstId { pub type ErasedAstId = crate::InFile; impl ErasedAstId { - pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr { + pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> SyntaxNodePtr { db.ast_id_map(self.file_id).get_erased(self.value) } } @@ -197,6 +199,19 @@ impl AstIdMap { FileAstId { raw, covariant: PhantomData } } + pub fn ast_id_for_ptr(&self, ptr: AstPtr) -> FileAstId { + let ptr = ptr.syntax_node_ptr(); + let hash = hash_ptr(&ptr); + match self.map.raw_entry().from_hash(hash, |&idx| self.arena[idx] == ptr) { + Some((&raw, &())) => FileAstId { raw, covariant: PhantomData }, + None => panic!( + "Can't find {:?} in AstIdMap:\n{:?}", + ptr, + self.arena.iter().map(|(_id, i)| i).collect::>(), + ), + } + } + pub fn get(&self, id: FileAstId) -> AstPtr { AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap() } diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index b8fc30c91189..bd0f81881ee4 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -1,19 +1,20 @@ //! A higher level attributes based on TokenTree, with also some shortcuts. use std::{fmt, ops}; -use base_db::{span::SyntaxContextId, CrateId}; +use base_db::CrateId; use cfg::CfgExpr; use either::Either; use intern::Interned; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use smallvec::{smallvec, SmallVec}; +use span::Span; use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; use triomphe::Arc; use crate::{ db::ExpandDatabase, mod_path::ModPath, - span::SpanMapRef, + span_map::SpanMapRef, tt::{self, Subtree}, InFile, }; @@ -52,7 +53,7 @@ impl RawAttrs { id, input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), path: Interned::new(ModPath::from(crate::name!(doc))), - ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx, + span: span_map.span_for_range(comment.syntax().text_range()), }), }); let entries: Arc<[Attr]> = Arc::from_iter(entries); @@ -119,7 +120,7 @@ impl RawAttrs { let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| { let tree = Subtree { - delimiter: tt::Delimiter::dummy_invisible(), + delimiter: tt::Delimiter::invisible_spanned(attr.first()?.first_span()), token_trees: attr.to_vec(), }; Attr::from_tt(db, &tree, index.with_cfg_attr(idx)) @@ -176,7 +177,7 @@ pub struct Attr { pub id: AttrId, pub path: Interned, pub input: Option>, - pub ctxt: SyntaxContextId, + pub span: Span, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -205,6 +206,7 @@ impl Attr { id: AttrId, ) -> Option { let path = Interned::new(ModPath::from_src(db, ast.path()?, span_map)?); + let span = span_map.span_for_range(ast.syntax().text_range()); let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { let value = match lit.kind() { ast::LiteralKind::String(string) => string.value()?.into(), @@ -212,12 +214,12 @@ impl Attr { }; Some(Interned::new(AttrInput::Literal(value))) } else if let Some(tt) = ast.token_tree() { - let tree = syntax_node_to_token_tree(tt.syntax(), span_map); + let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span); Some(Interned::new(AttrInput::TokenTree(Box::new(tree)))) } else { None }; - Some(Attr { id, path, input, ctxt: span_map.span_for_range(ast.syntax().text_range()).ctx }) + Some(Attr { id, path, input, span }) } fn from_tt(db: &dyn ExpandDatabase, tt: &tt::Subtree, id: AttrId) -> Option { @@ -265,7 +267,7 @@ impl Attr { pub fn parse_path_comma_token_tree<'a>( &'a self, db: &'a dyn ExpandDatabase, - ) -> Option + 'a> { + ) -> Option + 'a> { let args = self.token_tree_value()?; if args.delimiter.kind != DelimiterKind::Parenthesis { @@ -281,7 +283,7 @@ impl Attr { // FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation // here or maybe just parse a mod path from a token tree directly let subtree = tt::Subtree { - delimiter: tt::Delimiter::dummy_invisible(), + delimiter: tt::Delimiter::invisible_spanned(tts.first()?.first_span()), token_trees: tts.to_vec(), }; let (parse, span_map) = @@ -293,7 +295,7 @@ impl Attr { return None; } let path = meta.path()?; - let call_site = span_map.span_at(path.syntax().text_range().start()).ctx; + let call_site = span_map.span_at(path.syntax().text_range().start()); Some(( ModPath::from_src(db, path, SpanMapRef::ExpansionSpanMap(&span_map))?, call_site, diff --git a/crates/hir-expand/src/builtin_attr_macro.rs b/crates/hir-expand/src/builtin_attr_macro.rs index de58a495fef4..55157abe671a 100644 --- a/crates/hir-expand/src/builtin_attr_macro.rs +++ b/crates/hir-expand/src/builtin_attr_macro.rs @@ -1,12 +1,7 @@ //! Builtin attributes. +use span::{MacroCallId, Span}; -use base_db::{ - span::{SyntaxContextId, ROOT_ERASED_FILE_AST_ID}, - FileId, -}; -use syntax::{TextRange, TextSize}; - -use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallId, MacroCallKind}; +use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallKind}; macro_rules! register_builtin { ($expand_fn:ident: $(($name:ident, $variant:ident) => $expand:ident),* ) => { @@ -106,7 +101,12 @@ fn derive_attr_expand( MacroCallKind::Attr { attr_args: Some(attr_args), .. } if loc.def.is_attribute_derive() => { attr_args } - _ => return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan::DUMMY)), + _ => { + return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan { + open: loc.call_site, + close: loc.call_site, + })) + } }; pseudo_derive_attr_expansion(tt, derives, loc.call_site) } @@ -114,20 +114,13 @@ fn derive_attr_expand( pub fn pseudo_derive_attr_expansion( tt: &tt::Subtree, args: &tt::Subtree, - call_site: SyntaxContextId, + call_site: Span, ) -> ExpandResult { let mk_leaf = |char| { tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char, spacing: tt::Spacing::Alone, - span: tt::SpanData { - range: TextRange::empty(TextSize::new(0)), - anchor: base_db::span::SpanAnchor { - file_id: FileId::BOGUS, - ast_id: ROOT_ERASED_FILE_AST_ID, - }, - ctx: call_site, - }, + span: call_site, })) }; diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 410aa4d289eb..8f240ef07320 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -1,20 +1,21 @@ //! Builtin derives. -use base_db::{span::SpanData, CrateOrigin, LangCrateOrigin}; +use base_db::{CrateOrigin, LangCrateOrigin}; use itertools::izip; use rustc_hash::FxHashSet; +use span::{MacroCallId, Span}; use stdx::never; use tracing::debug; use crate::{ hygiene::span_with_def_site_ctxt, name::{AsName, Name}, - span::SpanMapRef, + span_map::SpanMapRef, tt, }; use syntax::ast::{self, AstNode, FieldList, HasAttrs, HasGenericParams, HasName, HasTypeBounds}; -use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId}; +use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult}; macro_rules! register_builtin { ( $($trait:ident => $expand:ident),* ) => { @@ -35,7 +36,7 @@ macro_rules! register_builtin { $( BuiltinDeriveExpander::$trait => $expand, )* }; - let span = db.lookup_intern_macro_call(id).span(db); + let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); expander(db, id, span, tt, token_map) } @@ -73,16 +74,16 @@ enum VariantShape { Unit, } -fn tuple_field_iterator(span: SpanData, n: usize) -> impl Iterator { +fn tuple_field_iterator(span: Span, n: usize) -> impl Iterator { (0..n).map(move |it| tt::Ident::new(format!("f{it}"), span)) } impl VariantShape { - fn as_pattern(&self, path: tt::Subtree, span: SpanData) -> tt::Subtree { + fn as_pattern(&self, path: tt::Subtree, span: Span) -> tt::Subtree { self.as_pattern_map(path, span, |it| quote!(span => #it)) } - fn field_names(&self, span: SpanData) -> Vec { + fn field_names(&self, span: Span) -> Vec { match self { VariantShape::Struct(s) => s.clone(), VariantShape::Tuple(n) => tuple_field_iterator(span, *n).collect(), @@ -93,7 +94,7 @@ impl VariantShape { fn as_pattern_map( &self, path: tt::Subtree, - span: SpanData, + span: Span, field_map: impl Fn(&tt::Ident) -> tt::Subtree, ) -> tt::Subtree { match self { @@ -143,11 +144,11 @@ enum AdtShape { } impl AdtShape { - fn as_pattern(&self, span: SpanData, name: &tt::Ident) -> Vec { + fn as_pattern(&self, span: Span, name: &tt::Ident) -> Vec { self.as_pattern_map(name, |it| quote!(span =>#it), span) } - fn field_names(&self, span: SpanData) -> Vec> { + fn field_names(&self, span: Span) -> Vec> { match self { AdtShape::Struct(s) => { vec![s.field_names(span)] @@ -166,7 +167,7 @@ impl AdtShape { &self, name: &tt::Ident, field_map: impl Fn(&tt::Ident) -> tt::Subtree, - span: SpanData, + span: Span, ) -> Vec { match self { AdtShape::Struct(s) => { @@ -199,7 +200,7 @@ struct BasicAdtInfo { fn parse_adt( tm: SpanMapRef<'_>, adt: &ast::Adt, - call_site: SpanData, + call_site: Span, ) -> Result { let (name, generic_param_list, shape) = match adt { ast::Adt::Struct(it) => ( @@ -245,7 +246,7 @@ fn parse_adt( match this { Some(it) => { param_type_set.insert(it.as_name()); - mbe::syntax_node_to_token_tree(it.syntax(), tm) + mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site) } None => { tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) @@ -253,15 +254,15 @@ fn parse_adt( } }; let bounds = match ¶m { - ast::TypeOrConstParam::Type(it) => { - it.type_bound_list().map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm)) - } + ast::TypeOrConstParam::Type(it) => it + .type_bound_list() + .map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site)), ast::TypeOrConstParam::Const(_) => None, }; let ty = if let ast::TypeOrConstParam::Const(param) = param { let ty = param .ty() - .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax(), tm)) + .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax(), tm, call_site)) .unwrap_or_else(|| { tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) }); @@ -297,7 +298,7 @@ fn parse_adt( let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name(); param_type_set.contains(&name).then_some(p) }) - .map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm)) + .map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site)) .collect(); let name_token = name_to_token(tm, name)?; Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types }) @@ -349,7 +350,7 @@ fn name_to_token( /// therefore does not get bound by the derived trait. fn expand_simple_derive( // FIXME: use - invoc_span: SpanData, + invoc_span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, trait_path: tt::Subtree, @@ -397,7 +398,7 @@ fn expand_simple_derive( ExpandResult::ok(expanded) } -fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: SpanData) -> tt::TokenTree { +fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: Span) -> tt::TokenTree { // FIXME: make hygiene works for builtin derive macro // such that $crate can be used here. let cg = db.crate_graph(); @@ -416,7 +417,7 @@ fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: SpanData) fn copy_expand( db: &dyn ExpandDatabase, id: MacroCallId, - span: SpanData, + span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, ) -> ExpandResult { @@ -427,7 +428,7 @@ fn copy_expand( fn clone_expand( db: &dyn ExpandDatabase, id: MacroCallId, - span: SpanData, + span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, ) -> ExpandResult { @@ -470,13 +471,13 @@ fn clone_expand( } /// This function exists since `quote! {span => => }` doesn't work. -fn fat_arrow(span: SpanData) -> tt::Subtree { +fn fat_arrow(span: Span) -> tt::Subtree { let eq = tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span }; quote! {span => #eq> } } /// This function exists since `quote! {span => && }` doesn't work. -fn and_and(span: SpanData) -> tt::Subtree { +fn and_and(span: Span) -> tt::Subtree { let and = tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span }; quote! {span => #and& } } @@ -484,7 +485,7 @@ fn and_and(span: SpanData) -> tt::Subtree { fn default_expand( db: &dyn ExpandDatabase, id: MacroCallId, - span: SpanData, + span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, ) -> ExpandResult { @@ -529,7 +530,7 @@ fn default_expand( fn debug_expand( db: &dyn ExpandDatabase, id: MacroCallId, - span: SpanData, + span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, ) -> ExpandResult { @@ -607,7 +608,7 @@ fn debug_expand( fn hash_expand( db: &dyn ExpandDatabase, id: MacroCallId, - span: SpanData, + span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, ) -> ExpandResult { @@ -660,7 +661,7 @@ fn hash_expand( fn eq_expand( db: &dyn ExpandDatabase, id: MacroCallId, - span: SpanData, + span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, ) -> ExpandResult { @@ -671,7 +672,7 @@ fn eq_expand( fn partial_eq_expand( db: &dyn ExpandDatabase, id: MacroCallId, - span: SpanData, + span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, ) -> ExpandResult { @@ -725,7 +726,7 @@ fn partial_eq_expand( fn self_and_other_patterns( adt: &BasicAdtInfo, name: &tt::Ident, - span: SpanData, + span: Span, ) -> (Vec, Vec) { let self_patterns = adt.shape.as_pattern_map( name, @@ -749,7 +750,7 @@ fn self_and_other_patterns( fn ord_expand( db: &dyn ExpandDatabase, id: MacroCallId, - span: SpanData, + span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, ) -> ExpandResult { @@ -760,7 +761,7 @@ fn ord_expand( left: tt::Subtree, right: tt::Subtree, rest: tt::Subtree, - span: SpanData, + span: Span, ) -> tt::Subtree { let fat_arrow1 = fat_arrow(span); let fat_arrow2 = fat_arrow(span); @@ -813,7 +814,7 @@ fn ord_expand( fn partial_ord_expand( db: &dyn ExpandDatabase, id: MacroCallId, - span: SpanData, + span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>, ) -> ExpandResult { @@ -824,7 +825,7 @@ fn partial_ord_expand( left: tt::Subtree, right: tt::Subtree, rest: tt::Subtree, - span: SpanData, + span: Span, ) -> tt::Subtree { let fat_arrow1 = fat_arrow(span); let fat_arrow2 = fat_arrow(span); diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index c8f04bfee54f..f99a89176233 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -1,13 +1,11 @@ //! Builtin macro -use base_db::{ - span::{SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}, - AnchoredPath, Edition, FileId, -}; +use base_db::{AnchoredPath, Edition, FileId}; use cfg::CfgExpr; use either::Either; use itertools::Itertools; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; +use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use syntax::{ ast::{self, AstToken}, SmolStr, @@ -15,10 +13,11 @@ use syntax::{ use crate::{ db::ExpandDatabase, - hygiene::span_with_def_site_ctxt, - name, quote, + hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, + name::{self, known}, + quote, tt::{self, DelimSpan}, - ExpandError, ExpandResult, HirFileIdExt, MacroCallId, MacroCallLoc, + ExpandError, ExpandResult, HirFileIdExt, MacroCallId, }; macro_rules! register_builtin { @@ -44,7 +43,7 @@ macro_rules! register_builtin { $( BuiltinFnLikeExpander::$kind => $expand, )* }; - let span = db.lookup_intern_macro_call(id).span(db); + let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); expander(db, id, tt, span) } @@ -61,7 +60,7 @@ macro_rules! register_builtin { $( EagerExpander::$e_kind => $e_expand, )* }; - let span = db.lookup_intern_macro_call(id).span(db); + let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); expander(db, id, tt, span) } @@ -109,6 +108,7 @@ register_builtin! { (format_args, FormatArgs) => format_args_expand, (const_format_args, ConstFormatArgs) => format_args_expand, (format_args_nl, FormatArgsNl) => format_args_nl_expand, + (quote, Quote) => quote_expand, EAGER: (compile_error, CompileError) => compile_error_expand, @@ -122,7 +122,7 @@ register_builtin! { (option_env, OptionEnv) => option_env_expand } -fn mk_pound(span: SpanData) -> tt::Subtree { +fn mk_pound(span: Span) -> tt::Subtree { crate::quote::IntoTt::to_subtree( vec![crate::tt::Leaf::Punct(crate::tt::Punct { char: '#', @@ -138,7 +138,7 @@ fn module_path_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { // Just return a dummy result. ExpandResult::ok(quote! {span => @@ -150,13 +150,13 @@ fn line_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { // dummy implementation for type-checking purposes // Note that `line!` and `column!` will never be implemented properly, as they are by definition // not incremental ExpandResult::ok(tt::Subtree { - delimiter: tt::Delimiter::dummy_invisible(), + delimiter: tt::Delimiter::invisible_spanned(span), token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text: "0u32".into(), span, @@ -168,7 +168,7 @@ fn log_syntax_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { ExpandResult::ok(quote! {span =>}) } @@ -177,7 +177,7 @@ fn trace_macros_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { ExpandResult::ok(quote! {span =>}) } @@ -186,7 +186,7 @@ fn stringify_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let pretty = ::tt::pretty(&tt.token_trees); @@ -198,32 +198,38 @@ fn stringify_expand( } fn assert_expand( - _db: &dyn ExpandDatabase, - _id: MacroCallId, + db: &dyn ExpandDatabase, + id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { - let args = parse_exprs_with_sep(tt, ','); + let call_site_span = span_with_call_site_ctxt(db, span, id); + let args = parse_exprs_with_sep(tt, ',', call_site_span); let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; let expanded = match &*args { [cond, panic_args @ ..] => { let comma = tt::Subtree { - delimiter: tt::Delimiter::dummy_invisible(), + delimiter: tt::Delimiter::invisible_spanned(call_site_span), token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', spacing: tt::Spacing::Alone, - span, + span: call_site_span, }))], }; let cond = cond.clone(); let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma); - quote! {span =>{ + let mac = if use_panic_2021(db, span) { + quote! {call_site_span => #dollar_crate::panic::panic_2021!(##panic_args) } + } else { + quote! {call_site_span => #dollar_crate::panic!(##panic_args) } + }; + quote! {call_site_span =>{ if !(#cond) { - #dollar_crate::panic!(##panic_args); + #mac; } }} } - [] => quote! {span =>{}}, + [] => quote! {call_site_span =>{}}, }; ExpandResult::ok(expanded) @@ -233,7 +239,7 @@ fn file_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { // FIXME: RA purposefully lacks knowledge of absolute file names // so just return "". @@ -250,7 +256,7 @@ fn format_args_expand( db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { format_args_expand_general(db, id, tt, "", span) } @@ -259,7 +265,7 @@ fn format_args_nl_expand( db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { format_args_expand_general(db, id, tt, "\\n", span) } @@ -270,7 +276,7 @@ fn format_args_expand_general( tt: &tt::Subtree, // FIXME: Make use of this so that mir interpretation works properly _end_string: &str, - span: SpanData, + span: Span, ) -> ExpandResult { let pound = mk_pound(span); let mut tt = tt.clone(); @@ -284,7 +290,7 @@ fn asm_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { // We expand all assembly snippets to `format_args!` invocations to get format syntax // highlighting for them. @@ -314,7 +320,7 @@ fn global_asm_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { // Expand to nothing (at item-level) ExpandResult::ok(quote! {span =>}) @@ -324,7 +330,7 @@ fn cfg_expand( db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let loc = db.lookup_intern_macro_call(id); let expr = CfgExpr::parse(tt); @@ -337,19 +343,25 @@ fn panic_expand( db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { - let loc: MacroCallLoc = db.lookup_intern_macro_call(id); let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; + let call_site_span = span_with_call_site_ctxt(db, span, id); + + let mac = + if use_panic_2021(db, call_site_span) { known::panic_2021 } else { known::panic_2015 }; + // Expand to a macro call `$crate::panic::panic_{edition}` - let mut call = if db.crate_graph()[loc.krate].edition >= Edition::Edition2021 { - quote!(span =>#dollar_crate::panic::panic_2021!) - } else { - quote!(span =>#dollar_crate::panic::panic_2015!) - }; + let mut call = quote!(call_site_span =>#dollar_crate::panic::#mac!); // Pass the original arguments - call.token_trees.push(tt::TokenTree::Subtree(tt.clone())); + let mut subtree = tt.clone(); + subtree.delimiter = tt::Delimiter { + open: call_site_span, + close: call_site_span, + kind: tt::DelimiterKind::Parenthesis, + }; + call.token_trees.push(tt::TokenTree::Subtree(subtree)); ExpandResult::ok(call) } @@ -357,22 +369,52 @@ fn unreachable_expand( db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { - let loc: MacroCallLoc = db.lookup_intern_macro_call(id); - // Expand to a macro call `$crate::panic::unreachable_{edition}` let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; - let mut call = if db.crate_graph()[loc.krate].edition >= Edition::Edition2021 { - quote!(span =>#dollar_crate::panic::unreachable_2021!) + let call_site_span = span_with_call_site_ctxt(db, span, id); + + let mac = if use_panic_2021(db, call_site_span) { + known::unreachable_2021 } else { - quote!(span =>#dollar_crate::panic::unreachable_2015!) + known::unreachable_2015 }; + // Expand to a macro call `$crate::panic::panic_{edition}` + let mut call = quote!(call_site_span =>#dollar_crate::panic::#mac!); + // Pass the original arguments - call.token_trees.push(tt::TokenTree::Subtree(tt.clone())); + let mut subtree = tt.clone(); + subtree.delimiter = tt::Delimiter { + open: call_site_span, + close: call_site_span, + kind: tt::DelimiterKind::Parenthesis, + }; + call.token_trees.push(tt::TokenTree::Subtree(subtree)); ExpandResult::ok(call) } +fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { + // To determine the edition, we check the first span up the expansion + // stack that does not have #[allow_internal_unstable(edition_panic)]. + // (To avoid using the edition of e.g. the assert!() or debug_assert!() definition.) + loop { + let Some(expn) = db.lookup_intern_syntax_context(span.ctx).outer_expn else { + break false; + }; + let expn = db.lookup_intern_macro_call(expn); + // FIXME: Record allow_internal_unstable in the macro def (not been done yet because it + // would consume quite a bit extra memory for all call locs...) + // if let Some(features) = expn.def.allow_internal_unstable { + // if features.iter().any(|&f| f == sym::edition_panic) { + // span = expn.call_site; + // continue; + // } + // } + break expn.def.edition >= Edition::Edition2021; + } +} + fn unquote_str(lit: &tt::Literal) -> Option { let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::String::cast(lit)?; @@ -395,7 +437,7 @@ fn compile_error_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let err = match &*tt.token_trees { [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) { @@ -412,7 +454,7 @@ fn concat_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let mut err = None; let mut text = String::new(); @@ -459,7 +501,7 @@ fn concat_bytes_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let mut bytes = Vec::new(); let mut err = None; @@ -543,7 +585,7 @@ fn concat_idents_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let mut err = None; let mut ident = String::new(); @@ -596,7 +638,7 @@ fn include_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let file_id = match include_input_to_file_id(db, arg_id, tt) { Ok(it) => it, @@ -629,11 +671,11 @@ fn include_bytes_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, _tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { // FIXME: actually read the file here if the user asked for macro expansion let res = tt::Subtree { - delimiter: tt::Delimiter::dummy_invisible(), + delimiter: tt::Delimiter::invisible_spanned(span), token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text: r#"b"""#.into(), span, @@ -646,7 +688,7 @@ fn include_str_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let path = match parse_string(tt) { Ok(it) => it, @@ -681,7 +723,7 @@ fn env_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let key = match parse_string(tt) { Ok(it) => it, @@ -713,7 +755,7 @@ fn option_env_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, - span: SpanData, + span: Span, ) -> ExpandResult { let key = match parse_string(tt) { Ok(it) => it, @@ -729,3 +771,15 @@ fn option_env_expand( ExpandResult::ok(expanded) } + +fn quote_expand( + _db: &dyn ExpandDatabase, + _arg_id: MacroCallId, + _tt: &tt::Subtree, + span: Span, +) -> ExpandResult { + ExpandResult::new( + tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), + ExpandError::other("quote! is not implemented"), + ) +} diff --git a/crates/hir-expand/src/change.rs b/crates/hir-expand/src/change.rs new file mode 100644 index 000000000000..67b7df198e93 --- /dev/null +++ b/crates/hir-expand/src/change.rs @@ -0,0 +1,42 @@ +//! Defines a unit of change that can applied to the database to get the next +//! state. Changes are transactional. +use base_db::{salsa::Durability, CrateGraph, FileChange, SourceDatabaseExt, SourceRoot}; +use span::FileId; +use triomphe::Arc; + +use crate::{db::ExpandDatabase, proc_macro::ProcMacros}; + +#[derive(Debug, Default)] +pub struct Change { + pub source_change: FileChange, + pub proc_macros: Option, +} + +impl Change { + pub fn new() -> Self { + Self::default() + } + + pub fn apply(self, db: &mut (impl ExpandDatabase + SourceDatabaseExt)) { + self.source_change.apply(db); + if let Some(proc_macros) = self.proc_macros { + db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH); + } + } + + pub fn change_file(&mut self, file_id: FileId, new_text: Option>) { + self.source_change.change_file(file_id, new_text) + } + + pub fn set_crate_graph(&mut self, graph: CrateGraph) { + self.source_change.set_crate_graph(graph) + } + + pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) { + self.proc_macros = Some(proc_macros); + } + + pub fn set_roots(&mut self, roots: Vec) { + self.source_change.set_roots(roots) + } +} diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index 935669d49b5b..f7a26e436dee 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -1,14 +1,16 @@ //! Defines database & queries for macro expansion. +use std::sync::OnceLock; + use base_db::{ salsa::{self, debug::DebugQueryTable}, - span::SyntaxContextId, - CrateId, Edition, FileId, SourceDatabase, + CrateId, Edition, FileId, SourceDatabase, VersionReq, }; use either::Either; use limit::Limit; use mbe::{syntax_node_to_token_tree, ValueResult}; use rustc_hash::FxHashSet; +use span::{Span, SyntaxContextId}; use syntax::{ ast::{self, HasAttrs}, AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, @@ -21,11 +23,16 @@ use crate::{ builtin_attr_macro::pseudo_derive_attr_expansion, builtin_fn_macro::EagerExpander, fixup::{self, reverse_fixups, SyntaxFixupUndoInfo}, - hygiene::{apply_mark, SyntaxContextData, Transparency}, - span::{RealSpanMap, SpanMap, SpanMapRef}, - tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, - ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, HirFileId, HirFileIdRepr, MacroCallId, - MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFileId, ProcMacroExpander, + hygiene::{ + apply_mark, span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt, + SyntaxContextData, Transparency, + }, + proc_macro::ProcMacros, + span_map::{RealSpanMap, SpanMap, SpanMapRef}, + tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, + CustomProcMacroExpander, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, + HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, + MacroFileId, }; /// Total limit on the number of tokens produced by any macro invocation. @@ -39,10 +46,13 @@ static TOKEN_LIMIT: Limit = Limit::new(1_048_576); #[derive(Debug, Clone, Eq, PartialEq)] /// Old-style `macro_rules` or the new macros 2.0 pub struct DeclarativeMacroExpander { - pub mac: mbe::DeclarativeMacro, + pub mac: mbe::DeclarativeMacro, pub transparency: Transparency, } +// FIXME: Remove this once we drop support for 1.76 +static REQUIREMENT: OnceLock = OnceLock::new(); + impl DeclarativeMacroExpander { pub fn expand( &self, @@ -50,25 +60,61 @@ impl DeclarativeMacroExpander { tt: tt::Subtree, call_id: MacroCallId, ) -> ExpandResult { + let loc = db.lookup_intern_macro_call(call_id); + let toolchain = &db.crate_graph()[loc.def.krate].toolchain; + let new_meta_vars = toolchain.as_ref().map_or(false, |version| { + REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( + &base_db::Version { + pre: base_db::Prerelease::EMPTY, + build: base_db::BuildMetadata::EMPTY, + major: version.major, + minor: version.minor, + patch: version.patch, + }, + ) + }); match self.mac.err() { Some(e) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan::DUMMY), + tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }), ExpandError::other(format!("invalid macro definition: {e}")), ), None => self .mac - .expand(&tt, |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency)) + .expand( + &tt, + |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency), + new_meta_vars, + loc.call_site, + ) .map_err(Into::into), } } - pub fn expand_unhygienic(&self, tt: tt::Subtree) -> ExpandResult { + pub fn expand_unhygienic( + &self, + db: &dyn ExpandDatabase, + tt: tt::Subtree, + krate: CrateId, + call_site: Span, + ) -> ExpandResult { + let toolchain = &db.crate_graph()[krate].toolchain; + let new_meta_vars = toolchain.as_ref().map_or(false, |version| { + REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( + &base_db::Version { + pre: base_db::Prerelease::EMPTY, + build: base_db::BuildMetadata::EMPTY, + major: version.major, + minor: version.minor, + patch: version.patch, + }, + ) + }); match self.mac.err() { Some(e) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan::DUMMY), + tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::other(format!("invalid macro definition: {e}")), ), - None => self.mac.expand(&tt, |_| ()).map_err(Into::into), + None => self.mac.expand(&tt, |_| (), new_meta_vars, call_site).map_err(Into::into), } } } @@ -86,11 +132,15 @@ pub enum TokenExpander { /// `derive(Copy)` and such. BuiltInDerive(BuiltinDeriveExpander), /// The thing we love the most here in rust-analyzer -- procedural macros. - ProcMacro(ProcMacroExpander), + ProcMacro(CustomProcMacroExpander), } #[salsa::query_group(ExpandDatabaseStorage)] pub trait ExpandDatabase: SourceDatabase { + /// The proc macros. + #[salsa::input] + fn proc_macros(&self) -> Arc; + fn ast_id_map(&self, file_id: HirFileId) -> Arc; /// Main public API -- parses a hir file, not caring whether it's a real @@ -164,7 +214,20 @@ pub fn span_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap { } pub fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc { - Arc::new(RealSpanMap::from_file(db, file_id)) + use syntax::ast::HasModuleItem; + let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)]; + let ast_id_map = db.ast_id_map(file_id.into()); + let tree = db.parse(file_id).tree(); + pairs.extend( + tree.items() + .map(|item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())), + ); + + Arc::new(RealSpanMap::from_file( + file_id, + pairs.into_boxed_slice(), + tree.syntax().text_range().end(), + )) } /// This expands the given macro call, but with different arguments. This is @@ -184,12 +247,13 @@ pub fn expand_speculative( // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match loc.kind { - MacroCallKind::FnLike { .. } => { - (mbe::syntax_node_to_token_tree(speculative_args, span_map), SyntaxFixupUndoInfo::NONE) - } + MacroCallKind::FnLike { .. } => ( + mbe::syntax_node_to_token_tree(speculative_args, span_map, loc.call_site), + SyntaxFixupUndoInfo::NONE, + ), MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => { let censor = censor_for_macro_input(&loc, speculative_args); - let mut fixups = fixup::fixup_syntax(span_map, speculative_args); + let mut fixups = fixup::fixup_syntax(span_map, speculative_args, loc.call_site); fixups.append.retain(|it, _| match it { syntax::NodeOrToken::Node(it) => !censor.contains(it), syntax::NodeOrToken::Token(_) => true, @@ -201,6 +265,7 @@ pub fn expand_speculative( span_map, fixups.append, fixups.remove, + loc.call_site, ), fixups.undo_info, ) @@ -222,8 +287,9 @@ pub fn expand_speculative( }?; match attr.token_tree() { Some(token_tree) => { - let mut tree = syntax_node_to_token_tree(token_tree.syntax(), span_map); - tree.delimiter = tt::Delimiter::DUMMY_INVISIBLE; + let mut tree = + syntax_node_to_token_tree(token_tree.syntax(), span_map, loc.call_site); + tree.delimiter = tt::Delimiter::invisible_spanned(loc.call_site); Some(tree) } @@ -237,17 +303,16 @@ pub fn expand_speculative( // Otherwise the expand query will fetch the non speculative attribute args and pass those instead. let mut speculative_expansion = match loc.def.kind { MacroDefKind::ProcMacro(expander, ..) => { - tt.delimiter = tt::Delimiter::DUMMY_INVISIBLE; - let call_site = loc.span(db); + tt.delimiter = tt::Delimiter::invisible_spanned(loc.call_site); expander.expand( db, loc.def.krate, loc.krate, &tt, attr_arg.as_ref(), - call_site, - call_site, - call_site, + span_with_def_site_ctxt(db, loc.def.span, actual_macro_call), + span_with_call_site_ctxt(db, loc.def.span, actual_macro_call), + span_with_mixed_site_ctxt(db, loc.def.span, actual_macro_call), ) } MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => { @@ -258,9 +323,12 @@ pub fn expand_speculative( let adt = ast::Adt::cast(speculative_args.clone()).unwrap(); expander.expand(db, actual_macro_call, &adt, span_map) } - MacroDefKind::Declarative(it) => { - db.decl_macro_expander(loc.krate, it).expand_unhygienic(tt) - } + MacroDefKind::Declarative(it) => db.decl_macro_expander(loc.krate, it).expand_unhygienic( + db, + tt, + loc.def.krate, + loc.call_site, + ), MacroDefKind::BuiltIn(it, _) => it.expand(db, actual_macro_call, &tt).map_err(Into::into), MacroDefKind::BuiltInEager(it, _) => { it.expand(db, actual_macro_call, &tt).map_err(Into::into) @@ -410,12 +478,13 @@ fn macro_arg( MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).to_node(&root).syntax().clone(), }; let (mut tt, undo_info) = match loc.kind { - MacroCallKind::FnLike { .. } => { - (mbe::syntax_node_to_token_tree(&syntax, map.as_ref()), SyntaxFixupUndoInfo::NONE) - } + MacroCallKind::FnLike { .. } => ( + mbe::syntax_node_to_token_tree(&syntax, map.as_ref(), loc.call_site), + SyntaxFixupUndoInfo::NONE, + ), MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => { let censor = censor_for_macro_input(&loc, &syntax); - let mut fixups = fixup::fixup_syntax(map.as_ref(), &syntax); + let mut fixups = fixup::fixup_syntax(map.as_ref(), &syntax, loc.call_site); fixups.append.retain(|it, _| match it { syntax::NodeOrToken::Node(it) => !censor.contains(it), syntax::NodeOrToken::Token(_) => true, @@ -427,6 +496,7 @@ fn macro_arg( map.as_ref(), fixups.append.clone(), fixups.remove.clone(), + loc.call_site, ); reverse_fixups(&mut tt, &fixups.undo_info); } @@ -436,6 +506,7 @@ fn macro_arg( map, fixups.append, fixups.remove, + loc.call_site, ), fixups.undo_info, ) @@ -444,7 +515,7 @@ fn macro_arg( if loc.def.is_proc_macro() { // proc macros expect their inputs without parentheses, MBEs expect it with them included - tt.delimiter = tt::Delimiter::DUMMY_INVISIBLE; + tt.delimiter.kind = tt::DelimiterKind::Invisible; } if matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) { @@ -506,7 +577,8 @@ fn decl_macro_expander( def_crate: CrateId, id: AstId, ) -> Arc { - let is_2021 = db.crate_graph()[def_crate].edition >= Edition::Edition2021; + let crate_data = &db.crate_graph()[def_crate]; + let is_2021 = crate_data.edition >= Edition::Edition2021; let (root, map) = parse_with_map(db, id.file_id); let root = root.syntax_node(); @@ -530,13 +602,29 @@ fn decl_macro_expander( _ => None, } }; + let toolchain = crate_data.toolchain.as_ref(); + let new_meta_vars = toolchain.as_ref().map_or(false, |version| { + REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( + &base_db::Version { + pre: base_db::Prerelease::EMPTY, + build: base_db::BuildMetadata::EMPTY, + major: version.major, + minor: version.minor, + patch: version.patch, + }, + ) + }); let (mac, transparency) = match id.to_ptr(db).to_node(&root) { ast::Macro::MacroRules(macro_rules) => ( match macro_rules.token_tree() { Some(arg) => { - let tt = mbe::syntax_node_to_token_tree(arg.syntax(), map.as_ref()); - let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021); + let tt = mbe::syntax_node_to_token_tree( + arg.syntax(), + map.as_ref(), + map.span_for_range(macro_rules.macro_rules_token().unwrap().text_range()), + ); + let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars); mac } None => mbe::DeclarativeMacro::from_err( @@ -549,8 +637,12 @@ fn decl_macro_expander( ast::Macro::MacroDef(macro_def) => ( match macro_def.body() { Some(arg) => { - let tt = mbe::syntax_node_to_token_tree(arg.syntax(), map.as_ref()); - let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021); + let tt = mbe::syntax_node_to_token_tree( + arg.syntax(), + map.as_ref(), + map.span_for_range(macro_def.macro_token().unwrap().text_range()), + ); + let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars); mac } None => mbe::DeclarativeMacro::from_err( @@ -601,7 +693,7 @@ fn macro_expand( let Some((macro_arg, undo_info)) = value else { return ExpandResult { value: Arc::new(tt::Subtree { - delimiter: tt::Delimiter::DUMMY_INVISIBLE, + delimiter: tt::Delimiter::invisible_spanned(loc.call_site), token_trees: Vec::new(), }), // FIXME: We should make sure to enforce an invariant that invalid macro @@ -660,7 +752,7 @@ fn macro_expand( // Skip checking token tree limit for include! macro call if !loc.def.is_include() { // Set a hard limit for the expanded tt - if let Err(value) = check_tt_count(&tt) { + if let Err(value) = check_tt_count(&tt, loc.call_site) { return value; } } @@ -673,7 +765,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult ExpandResult None, }; - let call_site = loc.span(db); let ExpandResult { value: mut tt, err } = expander.expand( db, loc.def.krate, loc.krate, ¯o_arg, attr_arg, - // FIXME - call_site, - call_site, - // FIXME - call_site, + span_with_def_site_ctxt(db, loc.def.span, id), + span_with_call_site_ctxt(db, loc.def.span, id), + span_with_mixed_site_ctxt(db, loc.def.span, id), ); // Set a hard limit for the expanded tt - if let Err(value) = check_tt_count(&tt) { + if let Err(value) = check_tt_count(&tt, loc.call_site) { return value; } @@ -730,12 +819,12 @@ fn token_tree_to_syntax_node( mbe::token_tree_to_syntax_node(tt, entry_point) } -fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult>> { +fn check_tt_count(tt: &tt::Subtree, call_site: Span) -> Result<(), ExpandResult>> { let count = tt.count(); if TOKEN_LIMIT.check(count).is_err() { Err(ExpandResult { value: Arc::new(tt::Subtree { - delimiter: tt::Delimiter::DUMMY_INVISIBLE, + delimiter: tt::Delimiter::invisible_spanned(call_site), token_trees: vec![], }), err: Some(ExpandError::other(format!( diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs index 8d55240aef57..da85c2ec7ac8 100644 --- a/crates/hir-expand/src/eager.rs +++ b/crates/hir-expand/src/eager.rs @@ -18,7 +18,8 @@ //! //! //! See the full discussion : -use base_db::{span::SyntaxContextId, CrateId}; +use base_db::CrateId; +use span::Span; use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent}; use triomphe::Arc; @@ -26,9 +27,9 @@ use crate::{ ast::{self, AstNode}, db::ExpandDatabase, mod_path::ModPath, - span::SpanMapRef, - EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, MacroCallId, - MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, + span_map::SpanMapRef, + EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, Intern, + MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, }; pub fn expand_eager_macro_input( @@ -36,7 +37,7 @@ pub fn expand_eager_macro_input( krate: CrateId, macro_call: InFile, def: MacroDefId, - call_site: SyntaxContextId, + call_site: Span, resolver: &dyn Fn(ModPath) -> Option, ) -> ExpandResult> { let ast_map = db.ast_id_map(macro_call.file_id); @@ -48,13 +49,14 @@ pub fn expand_eager_macro_input( // When `lazy_expand` is called, its *parent* file must already exist. // Here we store an eager macro id for the argument expanded subtree // for that purpose. - let arg_id = db.intern_macro_call(MacroCallLoc { + let arg_id = MacroCallLoc { def, krate, eager: None, kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr }, call_site, - }); + } + .intern(db); let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } = db.parse_macro_expansion(arg_id.as_macro_file()); @@ -81,9 +83,9 @@ pub fn expand_eager_macro_input( return ExpandResult { value: None, err }; }; - let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map); + let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, call_site); - subtree.delimiter = crate::tt::Delimiter::DUMMY_INVISIBLE; + subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible; let loc = MacroCallLoc { def, @@ -93,7 +95,7 @@ pub fn expand_eager_macro_input( call_site, }; - ExpandResult { value: Some(db.intern_macro_call(loc)), err } + ExpandResult { value: Some(loc.intern(db)), err } } fn lazy_expand( @@ -101,7 +103,7 @@ fn lazy_expand( def: &MacroDefId, macro_call: InFile, krate: CrateId, - call_site: SyntaxContextId, + call_site: Span, ) -> ExpandResult<(InFile>, Arc)> { let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); @@ -121,7 +123,7 @@ fn eager_macro_recur( mut offset: TextSize, curr: InFile, krate: CrateId, - call_site: SyntaxContextId, + call_site: Span, macro_resolver: &dyn Fn(ModPath) -> Option, ) -> ExpandResult> { let original = curr.value.clone_for_update(); diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs index 89f0685d5b67..d0a1bef11c3b 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -1,11 +1,8 @@ //! Things to wrap other things in file ids. use std::iter; -use base_db::{ - span::{HirFileId, HirFileIdRepr, MacroFileId, SyntaxContextId}, - FileId, FileRange, -}; use either::Either; +use span::{FileId, FileRange, HirFileId, HirFileIdRepr, MacroFileId, SyntaxContextId}; use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize}; use crate::{db, ExpansionInfo, MacroFileIdExt}; @@ -345,7 +342,7 @@ impl InFile { } impl InFile { - pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option> { + pub fn original_ast_node_rooted(self, db: &dyn db::ExpandDatabase) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, // as we don't have node inputs otherwise and therefore can't find an `N` node in the input let file_id = match self.file_id.repr() { diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 346cd39a7675..d241d94b8c40 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -1,23 +1,19 @@ //! To make attribute macros work reliably when typing, we need to take care to //! fix up syntax errors in the code we're passing to them. -use base_db::{ - span::{ErasedFileAstId, SpanAnchor, SpanData}, - FileId, -}; -use la_arena::RawIdx; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; +use span::{ErasedFileAstId, Span, SpanAnchor, SpanData, FIXUP_ERASED_FILE_AST_ID_MARKER}; use stdx::never; use syntax::{ ast::{self, AstNode, HasLoopBody}, match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize, }; use triomphe::Arc; -use tt::{Spacing, Span}; +use tt::Spacing; use crate::{ - span::SpanMapRef, + span_map::SpanMapRef, tt::{Ident, Leaf, Punct, Subtree}, }; @@ -42,28 +38,30 @@ impl SyntaxFixupUndoInfo { pub(crate) const NONE: Self = SyntaxFixupUndoInfo { original: None }; } -// censoring -> just don't convert the node -// replacement -> censor + append -// append -> insert a fake node, here we need to assemble some dummy span that we can figure out how -// to remove later -const FIXUP_DUMMY_FILE: FileId = FileId::from_raw(FileId::MAX_FILE_ID); -const FIXUP_DUMMY_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(RawIdx::from_u32(!0)); +// We mark spans with `FIXUP_DUMMY_AST_ID` to indicate that they are fake. +const FIXUP_DUMMY_AST_ID: ErasedFileAstId = FIXUP_ERASED_FILE_AST_ID_MARKER; const FIXUP_DUMMY_RANGE: TextRange = TextRange::empty(TextSize::new(0)); +// If the fake span has this range end, that means that the range start is an index into the +// `original` list in `SyntaxFixupUndoInfo`. const FIXUP_DUMMY_RANGE_END: TextSize = TextSize::new(!0); -pub(crate) fn fixup_syntax(span_map: SpanMapRef<'_>, node: &SyntaxNode) -> SyntaxFixups { +pub(crate) fn fixup_syntax( + span_map: SpanMapRef<'_>, + node: &SyntaxNode, + call_site: Span, +) -> SyntaxFixups { let mut append = FxHashMap::::default(); let mut remove = FxHashSet::::default(); let mut preorder = node.preorder(); let mut original = Vec::new(); let dummy_range = FIXUP_DUMMY_RANGE; - // we use a file id of `FileId(!0)` to signal a fake node, and the text range's start offset as - // the index into the replacement vec but only if the end points to !0 - let dummy_anchor = SpanAnchor { file_id: FIXUP_DUMMY_FILE, ast_id: FIXUP_DUMMY_AST_ID }; - let fake_span = |range| SpanData { - range: dummy_range, - anchor: dummy_anchor, - ctx: span_map.span_for_range(range).ctx, + let fake_span = |range| { + let span = span_map.span_for_range(range); + SpanData { + range: dummy_range, + anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor }, + ctx: span.ctx, + } }; while let Some(event) = preorder.next() { let syntax::WalkEvent::Enter(node) = event else { continue }; @@ -72,15 +70,16 @@ pub(crate) fn fixup_syntax(span_map: SpanMapRef<'_>, node: &SyntaxNode) -> Synta if can_handle_error(&node) && has_error_to_handle(&node) { remove.insert(node.clone().into()); // the node contains an error node, we have to completely replace it by something valid - let original_tree = mbe::syntax_node_to_token_tree(&node, span_map); + let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site); let idx = original.len() as u32; original.push(original_tree); + let span = span_map.span_for_range(node_range); let replacement = Leaf::Ident(Ident { text: "__ra_fixup".into(), span: SpanData { range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END), - anchor: dummy_anchor, - ctx: span_map.span_for_range(node_range).ctx, + anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor }, + ctx: span.ctx, }, }); append.insert(node.clone().into(), vec![replacement]); @@ -301,9 +300,10 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool { pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo) { let Some(undo_info) = undo_info.original.as_deref() else { return }; let undo_info = &**undo_info; + #[allow(deprecated)] if never!( - tt.delimiter.close.anchor.file_id == FIXUP_DUMMY_FILE - || tt.delimiter.open.anchor.file_id == FIXUP_DUMMY_FILE + tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID + || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID ) { tt.delimiter.close = SpanData::DUMMY; tt.delimiter.open = SpanData::DUMMY; @@ -319,7 +319,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { .filter(|tt| match tt { tt::TokenTree::Leaf(leaf) => { let span = leaf.span(); - let is_real_leaf = span.anchor.file_id != FIXUP_DUMMY_FILE; + let is_real_leaf = span.anchor.ast_id != FIXUP_DUMMY_AST_ID; let is_replaced_node = span.range.end() == FIXUP_DUMMY_RANGE_END; is_real_leaf || is_replaced_node } @@ -327,8 +327,8 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { }) .flat_map(|tt| match tt { tt::TokenTree::Subtree(mut tt) => { - if tt.delimiter.close.anchor.file_id == FIXUP_DUMMY_FILE - || tt.delimiter.open.anchor.file_id == FIXUP_DUMMY_FILE + if tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID + || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID { // Even though fixup never creates subtrees with fixup spans, the old proc-macro server // might copy them if the proc-macro asks for it, so we need to filter those out @@ -339,7 +339,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { SmallVec::from_const([tt.into()]) } tt::TokenTree::Leaf(leaf) => { - if leaf.span().anchor.file_id == FIXUP_DUMMY_FILE { + if leaf.span().anchor.ast_id == FIXUP_DUMMY_AST_ID { // we have a fake node here, we need to replace it again with the original let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone(); if original.delimiter.kind == tt::DelimiterKind::Invisible { @@ -360,11 +360,12 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { mod tests { use base_db::FileId; use expect_test::{expect, Expect}; + use syntax::TextRange; use triomphe::Arc; use crate::{ fixup::reverse_fixups, - span::{RealSpanMap, SpanMap}, + span_map::{RealSpanMap, SpanMap}, tt, }; @@ -397,12 +398,17 @@ mod tests { fn check(ra_fixture: &str, mut expect: Expect) { let parsed = syntax::SourceFile::parse(ra_fixture); let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0)))); - let fixups = super::fixup_syntax(span_map.as_ref(), &parsed.syntax_node()); + let fixups = super::fixup_syntax( + span_map.as_ref(), + &parsed.syntax_node(), + span_map.span_for_range(TextRange::empty(0.into())), + ); let mut tt = mbe::syntax_node_to_token_tree_modified( &parsed.syntax_node(), span_map.as_ref(), fixups.append, fixups.remove, + span_map.span_for_range(TextRange::empty(0.into())), ); let actual = format!("{tt}\n"); @@ -422,8 +428,11 @@ mod tests { // the fixed-up + reversed version should be equivalent to the original input // modulo token IDs and `Punct`s' spacing. - let original_as_tt = - mbe::syntax_node_to_token_tree(&parsed.syntax_node(), span_map.as_ref()); + let original_as_tt = mbe::syntax_node_to_token_tree( + &parsed.syntax_node(), + span_map.as_ref(), + span_map.span_for_range(TextRange::empty(0.into())), + ); assert!( check_subtree_eq(&tt, &original_as_tt), "different token tree:\n{tt:?}\n\n{original_as_tt:?}" diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs index 7b03709aced0..57921543c4b4 100644 --- a/crates/hir-expand/src/hygiene.rs +++ b/crates/hir-expand/src/hygiene.rs @@ -2,9 +2,12 @@ //! //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at //! this moment, this is horribly incomplete and handles only `$crate`. + +// FIXME: Consider moving this into the span crate. + use std::iter; -use base_db::span::{MacroCallId, SpanData, SyntaxContextId}; +use span::{MacroCallId, Span, SyntaxContextId}; use crate::db::ExpandDatabase; @@ -78,37 +81,29 @@ pub enum Transparency { Opaque, } -pub fn span_with_def_site_ctxt( - db: &dyn ExpandDatabase, - span: SpanData, - expn_id: MacroCallId, -) -> SpanData { +pub fn span_with_def_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span { span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque) } -pub fn span_with_call_site_ctxt( - db: &dyn ExpandDatabase, - span: SpanData, - expn_id: MacroCallId, -) -> SpanData { +pub fn span_with_call_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span { span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent) } pub fn span_with_mixed_site_ctxt( db: &dyn ExpandDatabase, - span: SpanData, + span: Span, expn_id: MacroCallId, -) -> SpanData { +) -> Span { span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent) } fn span_with_ctxt_from_mark( db: &dyn ExpandDatabase, - span: SpanData, + span: Span, expn_id: MacroCallId, transparency: Transparency, -) -> SpanData { - SpanData { ctx: apply_mark(db, SyntaxContextId::ROOT, expn_id, transparency), ..span } +) -> Span { + Span { ctx: apply_mark(db, SyntaxContextId::ROOT, expn_id, transparency), ..span } } pub(super) fn apply_mark( @@ -121,7 +116,7 @@ pub(super) fn apply_mark( return apply_mark_internal(db, ctxt, Some(call_id), transparency); } - let call_site_ctxt = db.lookup_intern_macro_call(call_id).call_site; + let call_site_ctxt = db.lookup_intern_macro_call(call_id).call_site.ctx; let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { call_site_ctxt.normalize_to_macros_2_0(db) } else { @@ -154,15 +149,16 @@ fn apply_mark_internal( transparency: Transparency, ) -> SyntaxContextId { let syntax_context_data = db.lookup_intern_syntax_context(ctxt); - let mut opaque = syntax_context_data.opaque; - let mut opaque_and_semitransparent = syntax_context_data.opaque_and_semitransparent; + let mut opaque = handle_self_ref(ctxt, syntax_context_data.opaque); + let mut opaque_and_semitransparent = + handle_self_ref(ctxt, syntax_context_data.opaque_and_semitransparent); if transparency >= Transparency::Opaque { let parent = opaque; + // Unlike rustc, with salsa we can't prefetch the to be allocated ID to create cycles with + // salsa when interning, so we use a sentinel value that effectively means the current + // syntax context. let new_opaque = SyntaxContextId::SELF_REF; - // But we can't just grab the to be allocated ID either as that would not deduplicate - // things! - // So we need a new salsa store type here ... opaque = db.intern_syntax_context(SyntaxContextData { outer_expn: call_id, outer_transparency: transparency, @@ -174,6 +170,9 @@ fn apply_mark_internal( if transparency >= Transparency::SemiTransparent { let parent = opaque_and_semitransparent; + // Unlike rustc, with salsa we can't prefetch the to be allocated ID to create cycles with + // salsa when interning, so we use a sentinel value that effectively means the current + // syntax context. let new_opaque_and_semitransparent = SyntaxContextId::SELF_REF; opaque_and_semitransparent = db.intern_syntax_context(SyntaxContextData { outer_expn: call_id, diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index d7819b315c49..b5197d4c25d2 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -6,20 +6,21 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -pub mod db; pub mod ast_id_map; -pub mod name; -pub mod hygiene; +pub mod attrs; pub mod builtin_attr_macro; pub mod builtin_derive_macro; pub mod builtin_fn_macro; +pub mod db; +pub mod eager; +pub mod files; +pub mod change; +pub mod hygiene; +pub mod mod_path; +pub mod name; pub mod proc_macro; pub mod quote; -pub mod eager; -pub mod mod_path; -pub mod attrs; -pub mod span; -pub mod files; +pub mod span_map; mod fixup; use attrs::collect_attrs; @@ -27,11 +28,9 @@ use triomphe::Arc; use std::{fmt, hash::Hash}; -use base_db::{ - span::{HirFileIdRepr, SpanData, SyntaxContextId}, - CrateId, FileId, FileRange, ProcMacroKind, -}; +use base_db::{CrateId, Edition, FileId}; use either::Either; +use span::{FileRange, HirFileIdRepr, Span, SyntaxContextId}; use syntax::{ ast::{self, AstNode}, SyntaxNode, SyntaxToken, TextRange, TextSize, @@ -42,35 +41,86 @@ use crate::{ builtin_attr_macro::BuiltinAttrExpander, builtin_derive_macro::BuiltinDeriveExpander, builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander}, - db::TokenExpander, + db::{ExpandDatabase, TokenExpander}, fixup::SyntaxFixupUndoInfo, + hygiene::SyntaxContextData, mod_path::ModPath, - proc_macro::ProcMacroExpander, - span::{ExpansionSpanMap, SpanMap}, + proc_macro::{CustomProcMacroExpander, ProcMacroKind}, + span_map::{ExpansionSpanMap, SpanMap}, }; pub use crate::ast_id_map::{AstId, ErasedAstId, ErasedFileAstId}; pub use crate::files::{InFile, InMacroFile, InRealFile}; -pub use base_db::span::{HirFileId, MacroCallId, MacroFileId}; pub use mbe::ValueResult; +pub use span::{HirFileId, MacroCallId, MacroFileId}; -pub type DeclarativeMacro = ::mbe::DeclarativeMacro; +pub type DeclarativeMacro = ::mbe::DeclarativeMacro; pub mod tt { - pub use base_db::span::SpanData; - pub use tt::{DelimiterKind, Spacing, Span, SpanAnchor}; + pub use span::Span; + pub use tt::{DelimiterKind, Spacing}; - pub type Delimiter = ::tt::Delimiter; - pub type DelimSpan = ::tt::DelimSpan; - pub type Subtree = ::tt::Subtree; - pub type Leaf = ::tt::Leaf; - pub type Literal = ::tt::Literal; - pub type Punct = ::tt::Punct; - pub type Ident = ::tt::Ident; - pub type TokenTree = ::tt::TokenTree; + pub type Delimiter = ::tt::Delimiter; + pub type DelimSpan = ::tt::DelimSpan; + pub type Subtree = ::tt::Subtree; + pub type Leaf = ::tt::Leaf; + pub type Literal = ::tt::Literal; + pub type Punct = ::tt::Punct; + pub type Ident = ::tt::Ident; + pub type TokenTree = ::tt::TokenTree; } +#[macro_export] +macro_rules! impl_intern_lookup { + ($db:ident, $id:ident, $loc:ident, $intern:ident, $lookup:ident) => { + impl $crate::Intern for $loc { + type Database<'db> = dyn $db + 'db; + type ID = $id; + fn intern<'db>(self, db: &Self::Database<'db>) -> $id { + db.$intern(self) + } + } + + impl $crate::Lookup for $id { + type Database<'db> = dyn $db + 'db; + type Data = $loc; + fn lookup<'db>(&self, db: &Self::Database<'db>) -> $loc { + db.$lookup(*self) + } + } + }; +} + +// ideally these would be defined in base-db, but the orphan rule doesn't let us +pub trait Intern { + type Database<'db>: ?Sized; + type ID; + fn intern<'db>(self, db: &Self::Database<'db>) -> Self::ID; +} + +pub trait Lookup { + type Database<'db>: ?Sized; + type Data; + fn lookup<'db>(&self, db: &Self::Database<'db>) -> Self::Data; +} + +impl_intern_lookup!( + ExpandDatabase, + MacroCallId, + MacroCallLoc, + intern_macro_call, + lookup_intern_macro_call +); + +impl_intern_lookup!( + ExpandDatabase, + SyntaxContextId, + SyntaxContextData, + intern_syntax_context, + lookup_intern_syntax_context +); + pub type ExpandResult = ValueResult; #[derive(Debug, PartialEq, Eq, Clone, Hash)] @@ -117,18 +167,20 @@ pub struct MacroCallLoc { pub krate: CrateId, /// Some if this is a macro call for an eager macro. Note that this is `None` /// for the eager input macro file. + // FIXME: This seems bad to save in an interned structure eager: Option>, pub kind: MacroCallKind, - pub call_site: SyntaxContextId, + pub call_site: Span, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroDefId { pub krate: CrateId, + pub edition: Edition, pub kind: MacroDefKind, pub local_inner: bool, pub allow_internal_unsafe: bool, - // pub def_site: SyntaxContextId, + pub span: Span, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -138,7 +190,7 @@ pub enum MacroDefKind { BuiltInAttr(BuiltinAttrExpander, AstId), BuiltInDerive(BuiltinDeriveExpander, AstId), BuiltInEager(EagerExpander, AstId), - ProcMacro(ProcMacroExpander, ProcMacroKind, AstId), + ProcMacro(CustomProcMacroExpander, ProcMacroKind, AstId), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -179,40 +231,39 @@ pub enum MacroCallKind { pub trait HirFileIdExt { /// Returns the original file of this macro call hierarchy. - fn original_file(self, db: &dyn db::ExpandDatabase) -> FileId; + fn original_file(self, db: &dyn ExpandDatabase) -> FileId; /// Returns the original file of this macro call hierarchy while going into the included file if /// one of the calls comes from an `include!``. - fn original_file_respecting_includes(self, db: &dyn db::ExpandDatabase) -> FileId; + fn original_file_respecting_includes(self, db: &dyn ExpandDatabase) -> FileId; /// If this is a macro call, returns the syntax node of the very first macro call this file resides in. - fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option>; + fn original_call_node(self, db: &dyn ExpandDatabase) -> Option>; /// Return expansion information if it is a macro-expansion file - fn expansion_info(self, db: &dyn db::ExpandDatabase) -> Option; + fn expansion_info(self, db: &dyn ExpandDatabase) -> Option; - fn as_builtin_derive_attr_node(&self, db: &dyn db::ExpandDatabase) - -> Option>; + fn as_builtin_derive_attr_node(&self, db: &dyn ExpandDatabase) -> Option>; } impl HirFileIdExt for HirFileId { - fn original_file(self, db: &dyn db::ExpandDatabase) -> FileId { + fn original_file(self, db: &dyn ExpandDatabase) -> FileId { let mut file_id = self; loop { match file_id.repr() { HirFileIdRepr::FileId(id) => break id, HirFileIdRepr::MacroFile(MacroFileId { macro_call_id }) => { - file_id = db.lookup_intern_macro_call(macro_call_id).kind.file_id(); + file_id = macro_call_id.lookup(db).kind.file_id(); } } } } - fn original_file_respecting_includes(mut self, db: &dyn db::ExpandDatabase) -> FileId { + fn original_file_respecting_includes(mut self, db: &dyn ExpandDatabase) -> FileId { loop { match self.repr() { - base_db::span::HirFileIdRepr::FileId(id) => break id, - base_db::span::HirFileIdRepr::MacroFile(file) => { + HirFileIdRepr::FileId(id) => break id, + HirFileIdRepr::MacroFile(file) => { let loc = db.lookup_intern_macro_call(file.macro_call_id); if loc.def.is_include() { if let Some(eager) = &loc.eager { @@ -231,7 +282,7 @@ impl HirFileIdExt for HirFileId { } } - fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option> { + fn original_call_node(self, db: &dyn ExpandDatabase) -> Option> { let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db); loop { match call.file_id.repr() { @@ -246,14 +297,11 @@ impl HirFileIdExt for HirFileId { } /// Return expansion information if it is a macro-expansion file - fn expansion_info(self, db: &dyn db::ExpandDatabase) -> Option { + fn expansion_info(self, db: &dyn ExpandDatabase) -> Option { Some(ExpansionInfo::new(db, self.macro_file()?)) } - fn as_builtin_derive_attr_node( - &self, - db: &dyn db::ExpandDatabase, - ) -> Option> { + fn as_builtin_derive_attr_node(&self, db: &dyn ExpandDatabase) -> Option> { let macro_file = self.macro_file()?; let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let attr = match loc.def.kind { @@ -265,32 +313,32 @@ impl HirFileIdExt for HirFileId { } pub trait MacroFileIdExt { - fn expansion_level(self, db: &dyn db::ExpandDatabase) -> u32; + fn expansion_level(self, db: &dyn ExpandDatabase) -> u32; /// If this is a macro call, returns the syntax node of the call. - fn call_node(self, db: &dyn db::ExpandDatabase) -> InFile; + fn call_node(self, db: &dyn ExpandDatabase) -> InFile; - fn expansion_info(self, db: &dyn db::ExpandDatabase) -> ExpansionInfo; + fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo; - fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool; - fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool; + fn is_builtin_derive(&self, db: &dyn ExpandDatabase) -> bool; + fn is_custom_derive(&self, db: &dyn ExpandDatabase) -> bool; /// Return whether this file is an include macro - fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool; + fn is_include_macro(&self, db: &dyn ExpandDatabase) -> bool; - fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool; + fn is_eager(&self, db: &dyn ExpandDatabase) -> bool; /// Return whether this file is an attr macro - fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool; + fn is_attr_macro(&self, db: &dyn ExpandDatabase) -> bool; /// Return whether this file is the pseudo expansion of the derive attribute. /// See [`crate::builtin_attr_macro::derive_attr_expand`]. - fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool; + fn is_derive_attr_pseudo_expansion(&self, db: &dyn ExpandDatabase) -> bool; } impl MacroFileIdExt for MacroFileId { - fn call_node(self, db: &dyn db::ExpandDatabase) -> InFile { + fn call_node(self, db: &dyn ExpandDatabase) -> InFile { db.lookup_intern_macro_call(self.macro_call_id).to_node(db) } - fn expansion_level(self, db: &dyn db::ExpandDatabase) -> u32 { + fn expansion_level(self, db: &dyn ExpandDatabase) -> u32 { let mut level = 0; let mut macro_file = self; loop { @@ -305,39 +353,39 @@ impl MacroFileIdExt for MacroFileId { } /// Return expansion information if it is a macro-expansion file - fn expansion_info(self, db: &dyn db::ExpandDatabase) -> ExpansionInfo { + fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo { ExpansionInfo::new(db, self) } - fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool { + fn is_custom_derive(&self, db: &dyn ExpandDatabase) -> bool { matches!( db.lookup_intern_macro_call(self.macro_call_id).def.kind, MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) ) } - fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool { + fn is_builtin_derive(&self, db: &dyn ExpandDatabase) -> bool { matches!( db.lookup_intern_macro_call(self.macro_call_id).def.kind, MacroDefKind::BuiltInDerive(..) ) } - fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool { + fn is_include_macro(&self, db: &dyn ExpandDatabase) -> bool { db.lookup_intern_macro_call(self.macro_call_id).def.is_include() } - fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool { + fn is_eager(&self, db: &dyn ExpandDatabase) -> bool { let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) } - fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool { + fn is_attr_macro(&self, db: &dyn ExpandDatabase) -> bool { let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); matches!(loc.kind, MacroCallKind::Attr { .. }) } - fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool { + fn is_derive_attr_pseudo_expansion(&self, db: &dyn ExpandDatabase) -> bool { let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); loc.def.is_attribute_derive() } @@ -346,15 +394,15 @@ impl MacroFileIdExt for MacroFileId { impl MacroDefId { pub fn as_lazy_macro( self, - db: &dyn db::ExpandDatabase, + db: &dyn ExpandDatabase, krate: CrateId, kind: MacroCallKind, - call_site: SyntaxContextId, + call_site: Span, ) -> MacroCallId { - db.intern_macro_call(MacroCallLoc { def: self, krate, eager: None, kind, call_site }) + MacroCallLoc { def: self, krate, eager: None, kind, call_site }.intern(db) } - pub fn definition_range(&self, db: &dyn db::ExpandDatabase) -> InFile { + pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile { match self.kind { MacroDefKind::Declarative(id) | MacroDefKind::BuiltIn(_, id) @@ -419,19 +467,7 @@ impl MacroDefId { } impl MacroCallLoc { - pub fn span(&self, db: &dyn db::ExpandDatabase) -> SpanData { - let ast_id = self.kind.erased_ast_id(); - let file_id = self.kind.file_id(); - let range = db.ast_id_map(file_id).get_erased(ast_id).text_range(); - match file_id.repr() { - HirFileIdRepr::FileId(file_id) => db.real_span_map(file_id).span_for_range(range), - HirFileIdRepr::MacroFile(m) => { - db.parse_macro_expansion(m).value.1.span_at(range.start()) - } - } - } - - pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile { + pub fn to_node(&self, db: &dyn ExpandDatabase) -> InFile { match self.kind { MacroCallKind::FnLike { ast_id, .. } => { ast_id.with_value(ast_id.to_node(db).syntax().clone()) @@ -498,7 +534,7 @@ impl MacroCallKind { } } - fn erased_ast_id(&self) -> ErasedFileAstId { + pub fn erased_ast_id(&self) -> ErasedFileAstId { match *self { MacroCallKind::FnLike { ast_id: InFile { value, .. }, .. } => value.erase(), MacroCallKind::Derive { ast_id: InFile { value, .. }, .. } => value.erase(), @@ -509,7 +545,7 @@ impl MacroCallKind { /// Returns the original file range that best describes the location of this macro call. /// /// Unlike `MacroCallKind::original_call_range`, this also spans the item of attributes and derives. - pub fn original_call_range_with_body(self, db: &dyn db::ExpandDatabase) -> FileRange { + pub fn original_call_range_with_body(self, db: &dyn ExpandDatabase) -> FileRange { let mut kind = self; let file_id = loop { match kind.file_id().repr() { @@ -534,7 +570,7 @@ impl MacroCallKind { /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros /// get the whole `ast::MacroCall`, attribute macros get the attribute's range, and derives /// get only the specific derive that is being referred to. - pub fn original_call_range(self, db: &dyn db::ExpandDatabase) -> FileRange { + pub fn original_call_range(self, db: &dyn ExpandDatabase) -> FileRange { let mut kind = self; let file_id = loop { match kind.file_id().repr() { @@ -573,7 +609,7 @@ impl MacroCallKind { FileRange { range, file_id } } - fn arg(&self, db: &dyn db::ExpandDatabase) -> InFile> { + fn arg(&self, db: &dyn ExpandDatabase) -> InFile> { match self { MacroCallKind::FnLike { ast_id, .. } => { ast_id.to_in_file_node(db).map(|it| Some(it.token_tree()?.syntax().clone())) @@ -617,7 +653,7 @@ impl ExpansionInfo { /// Maps the passed in file range down into a macro expansion if it is the input to a macro call. pub fn map_range_down<'a>( &'a self, - span: SpanData, + span: Span, ) -> Option + 'a>> { let tokens = self .exp_map @@ -630,7 +666,7 @@ impl ExpansionInfo { /// Looks up the span at the given offset. pub fn span_for_offset( &self, - db: &dyn db::ExpandDatabase, + db: &dyn ExpandDatabase, offset: TextSize, ) -> (FileRange, SyntaxContextId) { debug_assert!(self.expanded.value.text_range().contains(offset)); @@ -646,12 +682,12 @@ impl ExpansionInfo { /// Maps up the text range out of the expansion hierarchy back into the original file its from. pub fn map_node_range_up( &self, - db: &dyn db::ExpandDatabase, + db: &dyn ExpandDatabase, range: TextRange, ) -> Option<(FileRange, SyntaxContextId)> { debug_assert!(self.expanded.value.text_range().contains_range(range)); let mut spans = self.exp_map.spans_for_range(range); - let SpanData { range, anchor, ctx } = spans.next()?; + let Span { range, anchor, ctx } = spans.next()?; let mut start = range.start(); let mut end = range.end(); @@ -676,7 +712,7 @@ impl ExpansionInfo { /// Maps up the text range out of the expansion into is macro call. pub fn map_range_up_once( &self, - db: &dyn db::ExpandDatabase, + db: &dyn ExpandDatabase, token: TextRange, ) -> InFile> { debug_assert!(self.expanded.value.text_range().contains_range(token)); @@ -705,7 +741,7 @@ impl ExpansionInfo { } } - pub fn new(db: &dyn db::ExpandDatabase, macro_file: MacroFileId) -> ExpansionInfo { + pub fn new(db: &dyn ExpandDatabase, macro_file: MacroFileId) -> ExpansionInfo { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let arg_tt = loc.kind.arg(db); @@ -718,7 +754,7 @@ impl ExpansionInfo { let (macro_arg, _) = db.macro_arg(macro_file.macro_call_id).value.unwrap_or_else(|| { ( Arc::new(tt::Subtree { - delimiter: tt::Delimiter::DUMMY_INVISIBLE, + delimiter: tt::Delimiter::invisible_spanned(loc.call_site), token_trees: Vec::new(), }), SyntaxFixupUndoInfo::NONE, diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 9534b5039f68..30b8c189f52d 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -9,10 +9,11 @@ use crate::{ db::ExpandDatabase, hygiene::{marks_rev, SyntaxContextExt, Transparency}, name::{known, AsName, Name}, - span::SpanMapRef, + span_map::SpanMapRef, }; -use base_db::{span::SyntaxContextId, CrateId}; +use base_db::CrateId; use smallvec::SmallVec; +use span::SyntaxContextId; use syntax::{ast, AstNode}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index a321f94cd755..3d8d01e25566 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -318,6 +318,10 @@ pub mod known { new_lower_hex, new_upper_hex, from_usize, + panic_2015, + panic_2021, + unreachable_2015, + unreachable_2021, // Components of known path (type name) Iterator, IntoIterator, @@ -384,6 +388,7 @@ pub mod known { log_syntax, module_path, option_env, + quote, std_panic, stringify, trace_macros, diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs index de577796831f..25c78fade824 100644 --- a/crates/hir-expand/src/proc_macro.rs +++ b/crates/hir-expand/src/proc_macro.rs @@ -1,18 +1,64 @@ //! Proc Macro Expander stub -use base_db::{span::SpanData, CrateId, ProcMacroExpansionError, ProcMacroId, ProcMacroKind}; +use core::fmt; +use std::{panic::RefUnwindSafe, sync}; + +use base_db::{CrateId, Env}; +use rustc_hash::FxHashMap; +use span::Span; use stdx::never; +use syntax::SmolStr; use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult}; +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct ProcMacroId(pub u32); + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] +pub enum ProcMacroKind { + CustomDerive, + FuncLike, + Attr, +} + +pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { + fn expand( + &self, + subtree: &tt::Subtree, + attrs: Option<&tt::Subtree>, + env: &Env, + def_site: Span, + call_site: Span, + mixed_site: Span, + ) -> Result; +} + +#[derive(Debug)] +pub enum ProcMacroExpansionError { + Panic(String), + /// Things like "proc macro server was killed by OOM". + System(String), +} + +pub type ProcMacroLoadResult = Result, String>; + +pub type ProcMacros = FxHashMap; + +#[derive(Debug, Clone)] +pub struct ProcMacro { + pub name: SmolStr, + pub kind: ProcMacroKind, + pub expander: sync::Arc, +} + #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub struct ProcMacroExpander { +pub struct CustomProcMacroExpander { proc_macro_id: ProcMacroId, } const DUMMY_ID: u32 = !0; -impl ProcMacroExpander { +impl CustomProcMacroExpander { pub fn new(proc_macro_id: ProcMacroId) -> Self { assert_ne!(proc_macro_id.0, DUMMY_ID); Self { proc_macro_id } @@ -33,9 +79,9 @@ impl ProcMacroExpander { calling_crate: CrateId, tt: &tt::Subtree, attr_arg: Option<&tt::Subtree>, - def_site: SpanData, - call_site: SpanData, - mixed_site: SpanData, + def_site: Span, + call_site: Span, + mixed_site: Span, ) -> ExpandResult { match self.proc_macro_id { ProcMacroId(DUMMY_ID) => ExpandResult::new( diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs index acbde26c8ddf..9bdd75f9d224 100644 --- a/crates/hir-expand/src/quote.rs +++ b/crates/hir-expand/src/quote.rs @@ -1,6 +1,8 @@ //! A simplified version of quote-crate like quasi quote macro -use base_db::span::SpanData; +use span::Span; + +use crate::name::Name; // A helper macro quote macro // FIXME: @@ -130,12 +132,12 @@ macro_rules! quote { } pub(crate) trait IntoTt { - fn to_subtree(self, span: SpanData) -> crate::tt::Subtree; + fn to_subtree(self, span: Span) -> crate::tt::Subtree; fn to_tokens(self) -> Vec; } impl IntoTt for Vec { - fn to_subtree(self, span: SpanData) -> crate::tt::Subtree { + fn to_subtree(self, span: Span) -> crate::tt::Subtree { crate::tt::Subtree { delimiter: crate::tt::Delimiter::invisible_spanned(span), token_trees: self, @@ -148,7 +150,7 @@ impl IntoTt for Vec { } impl IntoTt for crate::tt::Subtree { - fn to_subtree(self, _: SpanData) -> crate::tt::Subtree { + fn to_subtree(self, _: Span) -> crate::tt::Subtree { self } @@ -158,39 +160,39 @@ impl IntoTt for crate::tt::Subtree { } pub(crate) trait ToTokenTree { - fn to_token(self, span: SpanData) -> crate::tt::TokenTree; + fn to_token(self, span: Span) -> crate::tt::TokenTree; } impl ToTokenTree for crate::tt::TokenTree { - fn to_token(self, _: SpanData) -> crate::tt::TokenTree { + fn to_token(self, _: Span) -> crate::tt::TokenTree { self } } impl ToTokenTree for &crate::tt::TokenTree { - fn to_token(self, _: SpanData) -> crate::tt::TokenTree { + fn to_token(self, _: Span) -> crate::tt::TokenTree { self.clone() } } impl ToTokenTree for crate::tt::Subtree { - fn to_token(self, _: SpanData) -> crate::tt::TokenTree { + fn to_token(self, _: Span) -> crate::tt::TokenTree { self.into() } } macro_rules! impl_to_to_tokentrees { - ($($span:ident: $ty:ty => $this:ident $im:block);*) => { + ($($span:ident: $ty:ty => $this:ident $im:block;)*) => { $( impl ToTokenTree for $ty { - fn to_token($this, $span: SpanData) -> crate::tt::TokenTree { + fn to_token($this, $span: Span) -> crate::tt::TokenTree { let leaf: crate::tt::Leaf = $im.into(); leaf.into() } } impl ToTokenTree for &$ty { - fn to_token($this, $span: SpanData) -> crate::tt::TokenTree { + fn to_token($this, $span: Span) -> crate::tt::TokenTree { let leaf: crate::tt::Leaf = $im.clone().into(); leaf.into() } @@ -209,20 +211,19 @@ impl_to_to_tokentrees! { _span: crate::tt::Ident => self { self }; _span: crate::tt::Punct => self { self }; span: &str => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span}}; - span: String => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span}} + span: String => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span}}; + span: Name => self { crate::tt::Ident{text: self.to_smol_str(), span}}; } #[cfg(test)] mod tests { use crate::tt; - use base_db::{ - span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}, - FileId, - }; + use base_db::FileId; use expect_test::expect; + use span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use syntax::{TextRange, TextSize}; - const DUMMY: tt::SpanData = tt::SpanData { + const DUMMY: tt::Span = tt::Span { range: TextRange::empty(TextSize::new(0)), anchor: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID }, ctx: SyntaxContextId::ROOT, diff --git a/crates/hir-expand/src/span.rs b/crates/hir-expand/src/span.rs deleted file mode 100644 index fe476a40febf..000000000000 --- a/crates/hir-expand/src/span.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! Spanmaps allow turning absolute ranges into relative ranges for incrementality purposes as well -//! as associating spans with text ranges in a particular file. -use base_db::{ - span::{ErasedFileAstId, SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}, - FileId, -}; -use syntax::{ast::HasModuleItem, AstNode, TextRange, TextSize}; -use triomphe::Arc; - -use crate::db::ExpandDatabase; - -pub type ExpansionSpanMap = mbe::SpanMap; - -/// Spanmap for a macro file or a real file -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SpanMap { - /// Spanmap for a macro file - ExpansionSpanMap(Arc), - /// Spanmap for a real file - RealSpanMap(Arc), -} - -#[derive(Copy, Clone)] -pub enum SpanMapRef<'a> { - /// Spanmap for a macro file - ExpansionSpanMap(&'a ExpansionSpanMap), - /// Spanmap for a real file - RealSpanMap(&'a RealSpanMap), -} - -impl mbe::SpanMapper for SpanMap { - fn span_for(&self, range: TextRange) -> SpanData { - self.span_for_range(range) - } -} -impl mbe::SpanMapper for SpanMapRef<'_> { - fn span_for(&self, range: TextRange) -> SpanData { - self.span_for_range(range) - } -} -impl mbe::SpanMapper for RealSpanMap { - fn span_for(&self, range: TextRange) -> SpanData { - self.span_for_range(range) - } -} - -impl SpanMap { - pub fn span_for_range(&self, range: TextRange) -> SpanData { - match self { - Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()), - Self::RealSpanMap(span_map) => span_map.span_for_range(range), - } - } - - pub fn as_ref(&self) -> SpanMapRef<'_> { - match self { - Self::ExpansionSpanMap(span_map) => SpanMapRef::ExpansionSpanMap(span_map), - Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map), - } - } -} - -impl SpanMapRef<'_> { - pub fn span_for_range(self, range: TextRange) -> SpanData { - match self { - Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()), - Self::RealSpanMap(span_map) => span_map.span_for_range(range), - } - } -} - -#[derive(PartialEq, Eq, Hash, Debug)] -pub struct RealSpanMap { - file_id: FileId, - /// Invariant: Sorted vec over TextSize - // FIXME: SortedVec<(TextSize, ErasedFileAstId)>? - pairs: Box<[(TextSize, ErasedFileAstId)]>, - end: TextSize, -} - -impl RealSpanMap { - /// Creates a real file span map that returns absolute ranges (relative ranges to the root ast id). - pub fn absolute(file_id: FileId) -> Self { - RealSpanMap { - file_id, - pairs: Box::from([(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)]), - end: TextSize::new(!0), - } - } - - pub fn from_file(db: &dyn ExpandDatabase, file_id: FileId) -> Self { - let mut pairs = vec![(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)]; - let ast_id_map = db.ast_id_map(file_id.into()); - let tree = db.parse(file_id).tree(); - pairs - .extend(tree.items().map(|item| { - (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase()) - })); - RealSpanMap { - file_id, - pairs: pairs.into_boxed_slice(), - end: tree.syntax().text_range().end(), - } - } - - pub fn span_for_range(&self, range: TextRange) -> SpanData { - assert!( - range.end() <= self.end, - "range {range:?} goes beyond the end of the file {:?}", - self.end - ); - let start = range.start(); - let idx = self - .pairs - .binary_search_by(|&(it, _)| it.cmp(&start).then(std::cmp::Ordering::Less)) - .unwrap_err(); - let (offset, ast_id) = self.pairs[idx - 1]; - SpanData { - range: range - offset, - anchor: SpanAnchor { file_id: self.file_id, ast_id }, - ctx: SyntaxContextId::ROOT, - } - } -} diff --git a/crates/hir-expand/src/span_map.rs b/crates/hir-expand/src/span_map.rs new file mode 100644 index 000000000000..4ec6e657f9ed --- /dev/null +++ b/crates/hir-expand/src/span_map.rs @@ -0,0 +1,65 @@ +//! Span maps for real files and macro expansions. +use span::Span; +use syntax::TextRange; +use triomphe::Arc; + +pub use span::RealSpanMap; + +pub type ExpansionSpanMap = span::SpanMap; + +/// Spanmap for a macro file or a real file +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SpanMap { + /// Spanmap for a macro file + ExpansionSpanMap(Arc), + /// Spanmap for a real file + RealSpanMap(Arc), +} + +#[derive(Copy, Clone)] +pub enum SpanMapRef<'a> { + /// Spanmap for a macro file + ExpansionSpanMap(&'a ExpansionSpanMap), + /// Spanmap for a real file + RealSpanMap(&'a RealSpanMap), +} + +impl mbe::SpanMapper for SpanMap { + fn span_for(&self, range: TextRange) -> Span { + self.span_for_range(range) + } +} +impl mbe::SpanMapper for SpanMapRef<'_> { + fn span_for(&self, range: TextRange) -> Span { + self.span_for_range(range) + } +} + +impl SpanMap { + pub fn span_for_range(&self, range: TextRange) -> Span { + match self { + // FIXME: Is it correct for us to only take the span at the start? This feels somewhat + // wrong. The context will be right, but the range could be considered wrong. See + // https://github.com/rust-lang/rust/issues/23480, we probably want to fetch the span at + // the start and end, then merge them like rustc does in `Span::to + Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()), + Self::RealSpanMap(span_map) => span_map.span_for_range(range), + } + } + + pub fn as_ref(&self) -> SpanMapRef<'_> { + match self { + Self::ExpansionSpanMap(span_map) => SpanMapRef::ExpansionSpanMap(span_map), + Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map), + } + } +} + +impl SpanMapRef<'_> { + pub fn span_for_range(self, range: TextRange) -> Span { + match self { + Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()), + Self::RealSpanMap(span_map) => span_map.span_for_range(range), + } + } +} diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index bbcb76a43ffe..1873e7bfe6a5 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -14,14 +14,14 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" itertools.workspace = true -arrayvec = "0.7.2" +arrayvec.workspace = true bitflags.workspace = true smallvec.workspace = true ena = "0.14.0" either.workspace = true oorandom = "11.1.3" tracing.workspace = true -rustc-hash = "1.1.0" +rustc-hash.workspace = true scoped-tls = "1.0.0" chalk-solve = { version = "0.95.0", default-features = false } chalk-ir = "0.95.0" @@ -54,6 +54,10 @@ project-model = { path = "../project-model" } # local deps test-utils.workspace = true +test-fixture.workspace = true [features] in-rust-tree = ["rustc-dependencies/in-rust-tree"] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index b395e7f4a813..ac82208708ae 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -1,6 +1,7 @@ -use base_db::{fixture::WithFixture, FileId}; +use base_db::FileId; use chalk_ir::Substitution; use hir_def::db::DefDatabase; +use test_fixture::WithFixture; use test_utils::skip_slow_tests; use crate::{ diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 6f724e458744..8053300ad220 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -217,6 +217,10 @@ pub enum InferenceDiagnostic { name: Name, /// Contains the type the field resolves to field_with_same_name: Option, + assoc_func_with_same_name: Option, + }, + UnresolvedAssocItem { + id: ExprOrPatId, }, // FIXME: This should be emitted in body lowering BreakOutsideOfLoop { @@ -1200,6 +1204,12 @@ impl<'a> InferenceContext<'a> { path: &ModPath, ) -> (Ty, Option) { let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0); + let ty = match ty.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + self.db.normalize_projection(proj_ty.clone(), self.table.trait_env.clone()) + } + _ => ty, + }; match remaining { None => { let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index a5e77a12d8c5..84954ca7e904 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -1575,11 +1575,30 @@ impl InferenceContext<'_> { } None => None, }; + + let assoc_func_with_same_name = method_resolution::iterate_method_candidates( + &canonicalized_receiver.value, + self.db, + self.table.trait_env.clone(), + self.get_traits_in_scope().as_ref().left_or_else(|&it| it), + VisibleFromModule::Filter(self.resolver.module()), + Some(method_name), + method_resolution::LookupMode::Path, + |_ty, item, visible| { + if visible { + Some(item) + } else { + None + } + }, + ); + self.result.diagnostics.push(InferenceDiagnostic::UnresolvedMethodCall { expr: tgt_expr, receiver: receiver_ty.clone(), name: method_name.clone(), field_with_same_name: field_with_same_name_exists, + assoc_func_with_same_name, }); ( receiver_ty, diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 49fb78f67a65..e61a070265a4 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -340,6 +340,9 @@ impl InferenceContext<'_> { }, ); let res = res.or(not_visible); + if res.is_none() { + self.push_diagnostic(InferenceDiagnostic::UnresolvedAssocItem { id }); + } let (item, visible) = res?; let (def, container) = match item { diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index 5e3a86c80e3f..9937113685ca 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; -use base_db::fixture::WithFixture; use chalk_ir::{AdtId, TyKind}; use either::Either; use hir_def::db::DefDatabase; +use test_fixture::WithFixture; use triomphe::Arc; use crate::{ diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index ff30dc6dade5..b0f929279a5c 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -1,6 +1,7 @@ -use base_db::{fixture::WithFixture, FileId}; +use base_db::FileId; use hir_def::db::DefDatabase; use syntax::{TextRange, TextSize}; +use test_fixture::WithFixture; use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution}; diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 1446e83fa887..c8cc61cc21b4 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -12,7 +12,7 @@ mod diagnostics; use std::{collections::HashMap, env}; -use base_db::{fixture::WithFixture, FileRange, SourceDatabaseExt}; +use base_db::{FileRange, SourceDatabaseExt}; use expect_test::Expect; use hir_def::{ body::{Body, BodySourceMap, SyntheticSyntax}, @@ -30,6 +30,7 @@ use syntax::{ ast::{self, AstNode, HasName}, SyntaxNode, }; +use test_fixture::WithFixture; use tracing_subscriber::{layer::SubscriberExt, Registry}; use tracing_tree::HierarchicalLayer; use triomphe::Arc; diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs index 28e84e480d77..82d934009f36 100644 --- a/crates/hir-ty/src/tests/incremental.rs +++ b/crates/hir-ty/src/tests/incremental.rs @@ -1,4 +1,5 @@ -use base_db::{fixture::WithFixture, SourceDatabaseExt}; +use base_db::SourceDatabaseExt; +use test_fixture::WithFixture; use triomphe::Arc; use crate::{db::HirDatabase, test_db::TestDB}; diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 7234af2d6834..548f782f4f2e 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -1154,6 +1154,40 @@ fn main() { ); } +#[test] +fn generic_alias_with_qualified_path() { + check_types( + r#" +type Wrap = T; + +struct S; + +trait Schematic { + type Props; +} + +impl Schematic for S { + type Props = X; +} + +enum X { + A { cool: u32, stuff: u32 }, + B, +} + +fn main() { + let wrapped = Wrap::<::Props>::A { + cool: 100, + stuff: 100, + }; + + if let Wrap::<::Props>::A { cool, ..} = &wrapped {} + //^^^^ &u32 +} +"#, + ); +} + #[test] fn type_mismatch_pat_const_reference() { check_no_mismatches( diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 4c1dfbc294e5..e4e4bcea6108 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml @@ -12,9 +12,9 @@ rust-version.workspace = true doctest = false [dependencies] -rustc-hash = "1.1.0" +rustc-hash.workspace = true either.workspace = true -arrayvec = "0.7.2" +arrayvec.workspace = true itertools.workspace = true smallvec.workspace = true triomphe.workspace = true @@ -33,3 +33,6 @@ tt.workspace = true [features] in-rust-tree = [] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 185853353181..d60d20f5b7ee 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -11,7 +11,7 @@ use hir_def::{ }; use hir_expand::{ name::Name, - span::{RealSpanMap, SpanMapRef}, + span_map::{RealSpanMap, SpanMapRef}, }; use hir_ty::db::HirDatabase; use syntax::{ast, AstNode}; diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index d98e3decd21e..7204868464b3 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs @@ -24,6 +24,6 @@ pub use hir_def::db::{ pub use hir_expand::db::{ AstIdMapQuery, DeclMacroExpanderQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, InternMacroCallQuery, InternSyntaxContextQuery, MacroArgQuery, - ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, RealSpanMapQuery, + ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, ProcMacrosQuery, RealSpanMapQuery, }; pub use hir_ty::db::*; diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 1cb36f9b021f..bf29a53913d1 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -8,7 +8,7 @@ pub use hir_ty::diagnostics::{CaseType, IncorrectCase}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use either::Either; -use hir_def::path::ModPath; +use hir_def::{path::ModPath, AssocItemId}; use hir_expand::{name::Name, HirFileId, InFile}; use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange}; @@ -62,6 +62,7 @@ diagnostics![ UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel, + UnresolvedAssocItem, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, @@ -215,6 +216,12 @@ pub struct UnresolvedMethodCall { pub receiver: Type, pub name: Name, pub field_with_same_name: Option, + pub assoc_func_with_same_name: Option, +} + +#[derive(Debug)] +pub struct UnresolvedAssocItem { + pub expr_or_pat: InFile>>>, } #[derive(Debug)] diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index e0230fa3761b..09b56e138241 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -37,7 +37,7 @@ mod display; use std::{iter, mem::discriminant, ops::ControlFlow}; use arrayvec::ArrayVec; -use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind}; +use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId}; use either::Either; use hir_def::{ body::{BodyDiagnostic, SyntheticSyntax}, @@ -47,7 +47,6 @@ use hir_def::{ item_tree::ItemTreeNode, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, - macro_id_to_def_id, nameres::{self, diagnostics::DefDiagnostic}, path::ImportAlias, per_ns::PerNs, @@ -59,7 +58,7 @@ use hir_def::{ Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, }; -use hir_expand::{attrs::collect_attrs, name::name, MacroCallKind}; +use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind}; use hir_ty::{ all_super_traits, autoderef, check_orphan_rules, consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, @@ -125,8 +124,10 @@ pub use { }, hir_expand::{ attrs::{Attr, AttrId}, + change::Change, hygiene::{marks_rev, SyntaxContextExt}, name::{known, Name}, + proc_macro::ProcMacros, tt, ExpandResult, HirFileId, HirFileIdExt, InFile, InMacroFile, InRealFile, MacroFileId, MacroFileIdExt, }, @@ -146,7 +147,7 @@ use { hir_def::path::Path, hir_expand::{ name::AsName, - span::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef}, + span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef}, }, }; @@ -808,7 +809,7 @@ impl Module { } fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec, m: Macro) { - let id = macro_id_to_def_id(db.upcast(), m.id); + let id = db.macro_def(m.id); if let hir_expand::db::TokenExpander::DeclarativeMacro(expander) = db.macro_expander(id) { if let Some(e) = expander.mac.err() { let Some(ast) = id.ast_id().left() else { @@ -1679,6 +1680,7 @@ impl DefWithBody { receiver, name, field_with_same_name, + assoc_func_with_same_name, } => { let expr = expr_syntax(*expr); @@ -1690,10 +1692,18 @@ impl DefWithBody { field_with_same_name: field_with_same_name .clone() .map(|ty| Type::new(db, DefWithBodyId::from(self), ty)), + assoc_func_with_same_name: assoc_func_with_same_name.clone(), } .into(), ) } + &hir_ty::InferenceDiagnostic::UnresolvedAssocItem { id } => { + let expr_or_pat = match id { + ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left), + ExprOrPatId::PatId(pat) => pat_syntax(pat).map(AstPtr::wrap_right), + }; + acc.push(UnresolvedAssocItem { expr_or_pat }.into()) + } &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, @@ -2784,9 +2794,13 @@ impl AsAssocItem for DefWithBody { } } -fn as_assoc_item(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option +fn as_assoc_item<'db, ID, DEF, CTOR, AST>( + db: &(dyn HirDatabase + 'db), + ctor: CTOR, + id: ID, +) -> Option where - ID: Lookup>, + ID: Lookup = dyn DefDatabase + 'db, Data = AssocItemLoc>, DEF: From, CTOR: FnOnce(DEF) -> AssocItem, AST: ItemTreeNode, @@ -3520,7 +3534,7 @@ impl Impl { let src = self.source(db)?; let macro_file = src.file_id.macro_file()?; - let loc = db.lookup_intern_macro_call(macro_file.macro_call_id); + let loc = macro_file.macro_call_id.lookup(db.upcast()); let (derive_attr, derive_index) = match loc.kind { MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => { let module_id = self.id.lookup(db.upcast()).container; @@ -4652,6 +4666,9 @@ impl Callable { pub fn return_type(&self) -> Type { self.ty.derived(self.sig.ret().clone()) } + pub fn sig(&self) -> &CallableSig { + &self.sig + } } fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option { diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index a03ff2207457..fdc604a006fc 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -13,7 +13,6 @@ use either::Either; use hir_def::{ hir::Expr, lower::LowerCtx, - macro_id_to_def_id, nameres::MacroSubNs, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, @@ -40,8 +39,8 @@ use crate::{ source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, - Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, ToolModule, Trait, Type, - TypeAlias, TypeParam, VariantDef, + Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Struct, ToolModule, Trait, + Type, TypeAlias, TypeParam, VariantDef, }; pub enum DescendPreference { @@ -229,6 +228,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn to_module_defs(&self, file: FileId) -> impl Iterator { self.imp.to_module_def(file) } + + pub fn to_struct_def(&self, s: &ast::Struct) -> Option { + self.imp.to_def(s).map(Struct::from) + } + + pub fn to_impl_def(&self, i: &ast::Impl) -> Option { + self.imp.to_def(i).map(Impl::from) + } } impl<'db> SemanticsImpl<'db> { @@ -341,9 +348,7 @@ impl<'db> SemanticsImpl<'db> { let macro_call = InFile::new(file_id, actual_macro_call); let krate = resolver.krate(); let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| { - resolver - .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang)) - .map(|(it, _)| macro_id_to_def_id(self.db.upcast(), it)) + resolver.resolve_path_as_macro_def(self.db.upcast(), &path, Some(MacroSubNs::Bang)) })?; hir_expand::db::expand_speculative( self.db.upcast(), @@ -512,8 +517,7 @@ impl<'db> SemanticsImpl<'db> { } /// Descend the token into its macro call if it is part of one, returning the tokens in the - /// expansion that it is associated with. If `offset` points into the token's range, it will - /// be considered for the mapping in case of inline format args. + /// expansion that it is associated with. pub fn descend_into_macros( &self, mode: DescendPreference, @@ -674,7 +678,7 @@ impl<'db> SemanticsImpl<'db> { _ => 0, }; // FIXME: here, the attribute's text range is used to strip away all - // entries from the start of the attribute "list" up the the invoking + // entries from the start of the attribute "list" up the invoking // attribute. But in // ``` // mod foo { @@ -850,7 +854,7 @@ impl<'db> SemanticsImpl<'db> { /// Attempts to map the node out of macro expanded files. /// This only work for attribute expansions, as other ones do not have nodes as input. pub fn original_ast_node(&self, node: N) -> Option { - self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map( + self.wrap_node_infile(node).original_ast_node_rooted(self.db.upcast()).map( |InRealFile { file_id, value }| { self.cache(find_root(value.syntax()), file_id.into()); value diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index d05118bbc28b..54b4d81012f3 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -16,7 +16,6 @@ use hir_def::{ hir::{BindingId, ExprId, Pat, PatId}, lang_item::LangItem, lower::LowerCtx, - macro_id_to_def_id, nameres::MacroSubNs, path::{ModPath, Path, PathKind}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, @@ -771,9 +770,7 @@ impl SourceAnalyzer { ) -> Option { let krate = self.resolver.krate(); let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| { - self.resolver - .resolve_path_as_macro(db.upcast(), &path, Some(MacroSubNs::Bang)) - .map(|(it, _)| macro_id_to_def_id(db.upcast(), it)) + self.resolver.resolve_path_as_macro_def(db.upcast(), &path, Some(MacroSubNs::Bang)) })?; // why the 64? Some(macro_call_id.as_macro_file()).filter(|it| it.expansion_level(db.upcast()) < 64) @@ -1163,9 +1160,40 @@ fn resolve_hir_path_qualifier( resolver: &Resolver, path: &Path, ) -> Option { - resolver - .resolve_path_in_type_ns_fully(db.upcast(), &path) - .map(|ty| match ty { + (|| { + let (ty, unresolved) = match path.type_anchor() { + Some(type_ref) => { + let (_, res) = + TyLoweringContext::new_maybe_unowned(db, resolver, resolver.type_owner()) + .lower_ty_ext(type_ref); + res.map(|ty_ns| (ty_ns, path.segments().first())) + } + None => { + let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db.upcast(), path)?; + match remaining_idx { + Some(remaining_idx) => { + if remaining_idx + 1 == path.segments().len() { + Some((ty, path.segments().last())) + } else { + None + } + } + None => Some((ty, None)), + } + } + }?; + + // If we are in a TypeNs for a Trait, and we have an unresolved name, try to resolve it as a type + // within the trait's associated types. + if let (Some(unresolved), &TypeNs::TraitId(trait_id)) = (&unresolved, &ty) { + if let Some(type_alias_id) = + db.trait_data(trait_id).associated_type_by_name(unresolved.name) + { + return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into())); + } + } + + let res = match ty { TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()), TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { @@ -1176,11 +1204,28 @@ fn resolve_hir_path_qualifier( TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()), - }) - .or_else(|| { - resolver - .resolve_module_path_in_items(db.upcast(), path.mod_path()?) - .take_types() - .map(|it| PathResolution::Def(it.into())) - }) + }; + match unresolved { + Some(unresolved) => resolver + .generic_def() + .and_then(|def| { + hir_ty::associated_type_shorthand_candidates( + db, + def, + res.in_type_ns()?, + |name, id| (name == unresolved.name).then_some(id), + ) + }) + .map(TypeAlias::from) + .map(Into::into) + .map(PathResolution::Def), + None => Some(res), + } + })() + .or_else(|| { + resolver + .resolve_module_path_in_items(db.upcast(), path.mod_path()?) + .take_types() + .map(|it| PathResolution::Def(it.into())) + }) } diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index a2a30edeb039..4da0dfba6755 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -2,13 +2,14 @@ use base_db::FileRange; use hir_def::{ + db::DefDatabase, item_scope::ItemInNs, src::{HasChildSource, HasSource}, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, }; use hir_expand::{HirFileId, InFile}; -use hir_ty::db::HirDatabase; +use hir_ty::{db::HirDatabase, display::HirDisplay}; use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr}; use crate::{Module, ModuleDef, Semantics}; @@ -230,9 +231,12 @@ impl<'a> SymbolCollector<'a> { fn collect_from_impl(&mut self, impl_id: ImplId) { let impl_data = self.db.impl_data(impl_id); - for &assoc_item_id in &impl_data.items { - self.push_assoc_item(assoc_item_id) - } + let impl_name = Some(SmolStr::new(impl_data.self_ty.display(self.db).to_string())); + self.with_container_name(impl_name, |s| { + for &assoc_item_id in &impl_data.items { + s.push_assoc_item(assoc_item_id) + } + }) } fn collect_from_trait(&mut self, trait_id: TraitId) { @@ -274,9 +278,9 @@ impl<'a> SymbolCollector<'a> { } } - fn push_decl(&mut self, id: L, is_assoc: bool) + fn push_decl<'db, L>(&mut self, id: L, is_assoc: bool) where - L: Lookup + Into, + L: Lookup = dyn DefDatabase + 'db> + Into, ::Data: HasSource, <::Data as HasSource>::Value: HasName, { diff --git a/crates/ide-assists/Cargo.toml b/crates/ide-assists/Cargo.toml index a622ec1a9532..4d4bac5fb966 100644 --- a/crates/ide-assists/Cargo.toml +++ b/crates/ide-assists/Cargo.toml @@ -31,7 +31,11 @@ expect-test = "1.4.0" # local deps test-utils.workspace = true +test-fixture.workspace = true sourcegen.workspace = true [features] in-rust-tree = [] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index f508c42c53e4..1f785b5d0a81 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -281,11 +281,8 @@ mod tests { use super::*; use hir::Semantics; - use ide_db::{ - assists::AssistResolveStrategy, - base_db::{fixture::WithFixture, FileRange}, - RootDatabase, - }; + use ide_db::{assists::AssistResolveStrategy, base_db::FileRange, RootDatabase}; + use test_fixture::WithFixture; use crate::tests::{ check_assist, check_assist_by_label, check_assist_not_applicable, check_assist_target, diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs index 0f2d1057c0a4..b7b00e7ed068 100644 --- a/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -16,11 +16,14 @@ use syntax::{ edit_in_place::{AttrsOwnerEdit, Indent}, make, HasName, }, - ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, + AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, }; use text_edit::TextRange; -use crate::assist_context::{AssistContext, Assists}; +use crate::{ + assist_context::{AssistContext, Assists}, + utils, +}; // Assist: bool_to_enum // @@ -73,7 +76,7 @@ pub(crate) fn bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let usages = definition.usages(&ctx.sema).all(); add_enum_def(edit, ctx, &usages, target_node, &target_module); - replace_usages(edit, ctx, &usages, definition, &target_module); + replace_usages(edit, ctx, usages, definition, &target_module); }, ) } @@ -169,8 +172,8 @@ fn replace_bool_expr(edit: &mut SourceChangeBuilder, expr: ast::Expr) { /// Converts an expression of type `bool` to one of the new enum type. fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr { - let true_expr = make::expr_path(make::path_from_text("Bool::True")).clone_for_update(); - let false_expr = make::expr_path(make::path_from_text("Bool::False")).clone_for_update(); + let true_expr = make::expr_path(make::path_from_text("Bool::True")); + let false_expr = make::expr_path(make::path_from_text("Bool::False")); if let ast::Expr::Literal(literal) = &expr { match literal.kind() { @@ -184,7 +187,6 @@ fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr { make::tail_only_block_expr(true_expr), Some(ast::ElseBranch::Block(make::tail_only_block_expr(false_expr))), ) - .clone_for_update() } } @@ -192,21 +194,19 @@ fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr { fn replace_usages( edit: &mut SourceChangeBuilder, ctx: &AssistContext<'_>, - usages: &UsageSearchResult, + usages: UsageSearchResult, target_definition: Definition, target_module: &hir::Module, ) { - for (file_id, references) in usages.iter() { - edit.edit_file(*file_id); + for (file_id, references) in usages { + edit.edit_file(file_id); - let refs_with_imports = - augment_references_with_imports(edit, ctx, references, target_module); + let refs_with_imports = augment_references_with_imports(ctx, references, target_module); refs_with_imports.into_iter().rev().for_each( - |FileReferenceWithImport { range, old_name, new_name, import_data }| { + |FileReferenceWithImport { range, name, import_data }| { // replace the usages in patterns and expressions - if let Some(ident_pat) = old_name.syntax().ancestors().find_map(ast::IdentPat::cast) - { + if let Some(ident_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) { cov_mark::hit!(replaces_record_pat_shorthand); let definition = ctx.sema.to_def(&ident_pat).map(Definition::Local); @@ -214,36 +214,35 @@ fn replace_usages( replace_usages( edit, ctx, - &def.usages(&ctx.sema).all(), + def.usages(&ctx.sema).all(), target_definition, target_module, ) } - } else if let Some(initializer) = find_assignment_usage(&new_name) { + } else if let Some(initializer) = find_assignment_usage(&name) { cov_mark::hit!(replaces_assignment); replace_bool_expr(edit, initializer); - } else if let Some((prefix_expr, inner_expr)) = find_negated_usage(&new_name) { + } else if let Some((prefix_expr, inner_expr)) = find_negated_usage(&name) { cov_mark::hit!(replaces_negation); edit.replace( prefix_expr.syntax().text_range(), format!("{} == Bool::False", inner_expr), ); - } else if let Some((record_field, initializer)) = old_name + } else if let Some((record_field, initializer)) = name .as_name_ref() .and_then(ast::RecordExprField::for_field_name) .and_then(|record_field| ctx.sema.resolve_record_field(&record_field)) .and_then(|(got_field, _, _)| { - find_record_expr_usage(&new_name, got_field, target_definition) + find_record_expr_usage(&name, got_field, target_definition) }) { cov_mark::hit!(replaces_record_expr); - let record_field = edit.make_mut(record_field); let enum_expr = bool_expr_to_enum_expr(initializer); - record_field.replace_expr(enum_expr); - } else if let Some(pat) = find_record_pat_field_usage(&old_name) { + utils::replace_record_field_expr(ctx, edit, record_field, enum_expr); + } else if let Some(pat) = find_record_pat_field_usage(&name) { match pat { ast::Pat::IdentPat(ident_pat) => { cov_mark::hit!(replaces_record_pat); @@ -253,7 +252,7 @@ fn replace_usages( replace_usages( edit, ctx, - &def.usages(&ctx.sema).all(), + def.usages(&ctx.sema).all(), target_definition, target_module, ) @@ -270,40 +269,44 @@ fn replace_usages( } _ => (), } - } else if let Some((ty_annotation, initializer)) = find_assoc_const_usage(&new_name) - { + } else if let Some((ty_annotation, initializer)) = find_assoc_const_usage(&name) { edit.replace(ty_annotation.syntax().text_range(), "Bool"); replace_bool_expr(edit, initializer); - } else if let Some(receiver) = find_method_call_expr_usage(&new_name) { + } else if let Some(receiver) = find_method_call_expr_usage(&name) { edit.replace( receiver.syntax().text_range(), format!("({} == Bool::True)", receiver), ); - } else if new_name.syntax().ancestors().find_map(ast::UseTree::cast).is_none() { + } else if name.syntax().ancestors().find_map(ast::UseTree::cast).is_none() { // for any other usage in an expression, replace it with a check that it is the true variant - if let Some((record_field, expr)) = new_name - .as_name_ref() - .and_then(ast::RecordExprField::for_field_name) - .and_then(|record_field| { - record_field.expr().map(|expr| (record_field, expr)) - }) + if let Some((record_field, expr)) = + name.as_name_ref().and_then(ast::RecordExprField::for_field_name).and_then( + |record_field| record_field.expr().map(|expr| (record_field, expr)), + ) { - record_field.replace_expr( + utils::replace_record_field_expr( + ctx, + edit, + record_field, make::expr_bin_op( expr, ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated: false }), make::expr_path(make::path_from_text("Bool::True")), - ) - .clone_for_update(), + ), ); } else { - edit.replace(range, format!("{} == Bool::True", new_name.text())); + edit.replace(range, format!("{} == Bool::True", name.text())); } } // add imports across modules where needed if let Some((import_scope, path)) = import_data { - insert_use(&import_scope, path, &ctx.config.insert_use); + let scope = match import_scope.clone() { + ImportScope::File(it) => ImportScope::File(edit.make_mut(it)), + ImportScope::Module(it) => ImportScope::Module(edit.make_mut(it)), + ImportScope::Block(it) => ImportScope::Block(edit.make_mut(it)), + }; + insert_use(&scope, path, &ctx.config.insert_use); } }, ) @@ -312,37 +315,31 @@ fn replace_usages( struct FileReferenceWithImport { range: TextRange, - old_name: ast::NameLike, - new_name: ast::NameLike, + name: ast::NameLike, import_data: Option<(ImportScope, ast::Path)>, } fn augment_references_with_imports( - edit: &mut SourceChangeBuilder, ctx: &AssistContext<'_>, - references: &[FileReference], + references: Vec, target_module: &hir::Module, ) -> Vec { let mut visited_modules = FxHashSet::default(); references - .iter() + .into_iter() .filter_map(|FileReference { range, name, .. }| { let name = name.clone().into_name_like()?; - ctx.sema.scope(name.syntax()).map(|scope| (*range, name, scope.module())) + ctx.sema.scope(name.syntax()).map(|scope| (range, name, scope.module())) }) .map(|(range, name, ref_module)| { - let old_name = name.clone(); - let new_name = edit.make_mut(name.clone()); - // if the referenced module is not the same as the target one and has not been seen before, add an import let import_data = if ref_module.nearest_non_block_module(ctx.db()) != *target_module && !visited_modules.contains(&ref_module) { visited_modules.insert(ref_module); - let import_scope = - ImportScope::find_insert_use_container(new_name.syntax(), &ctx.sema); + let import_scope = ImportScope::find_insert_use_container(name.syntax(), &ctx.sema); let path = ref_module .find_use_path_prefixed( ctx.sema.db, @@ -360,7 +357,7 @@ fn augment_references_with_imports( None }; - FileReferenceWithImport { range, old_name, new_name, import_data } + FileReferenceWithImport { range, name, import_data } }) .collect() } @@ -405,13 +402,12 @@ fn find_record_expr_usage( let record_field = ast::RecordExprField::for_field_name(name_ref)?; let initializer = record_field.expr()?; - if let Definition::Field(expected_field) = target_definition { - if got_field != expected_field { - return None; + match target_definition { + Definition::Field(expected_field) if got_field == expected_field => { + Some((record_field, initializer)) } + _ => None, } - - Some((record_field, initializer)) } fn find_record_pat_field_usage(name: &ast::NameLike) -> Option { @@ -466,12 +462,9 @@ fn add_enum_def( let indent = IndentLevel::from_node(&insert_before); enum_def.reindent_to(indent); - ted::insert_all( - ted::Position::before(&edit.make_syntax_mut(insert_before)), - vec![ - enum_def.syntax().clone().into(), - make::tokens::whitespace(&format!("\n\n{indent}")).into(), - ], + edit.insert( + insert_before.text_range().start(), + format!("{}\n\n{indent}", enum_def.syntax().text()), ); } @@ -800,6 +793,78 @@ fn main() { ) } + #[test] + fn local_var_init_struct_usage() { + check_assist( + bool_to_enum, + r#" +struct Foo { + foo: bool, +} + +fn main() { + let $0foo = true; + let s = Foo { foo }; +} +"#, + r#" +struct Foo { + foo: bool, +} + +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let foo = Bool::True; + let s = Foo { foo: foo == Bool::True }; +} +"#, + ) + } + + #[test] + fn local_var_init_struct_usage_in_macro() { + check_assist( + bool_to_enum, + r#" +struct Struct { + boolean: bool, +} + +macro_rules! identity { + ($body:expr) => { + $body + } +} + +fn new() -> Struct { + let $0boolean = true; + identity![Struct { boolean }] +} +"#, + r#" +struct Struct { + boolean: bool, +} + +macro_rules! identity { + ($body:expr) => { + $body + } +} + +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn new() -> Struct { + let boolean = Bool::True; + identity![Struct { boolean: boolean == Bool::True }] +} +"#, + ) + } + #[test] fn field_struct_basic() { cov_mark::check!(replaces_record_expr); @@ -1321,6 +1386,46 @@ fn main() { ) } + #[test] + fn field_in_macro() { + check_assist( + bool_to_enum, + r#" +struct Struct { + $0boolean: bool, +} + +fn boolean(x: Struct) { + let Struct { boolean } = x; +} + +macro_rules! identity { ($body:expr) => { $body } } + +fn new() -> Struct { + identity!(Struct { boolean: true }) +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +struct Struct { + boolean: Bool, +} + +fn boolean(x: Struct) { + let Struct { boolean } = x; +} + +macro_rules! identity { ($body:expr) => { $body } } + +fn new() -> Struct { + identity!(Struct { boolean: Bool::True }) +} +"#, + ) + } + #[test] fn field_non_bool() { cov_mark::check!(not_applicable_non_bool_field); diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index e7c884dcb706..874b81d3b637 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs @@ -1,12 +1,8 @@ use hir::TypeInfo; -use stdx::format_to; use syntax::{ - ast::{self, AstNode}, - NodeOrToken, - SyntaxKind::{ - BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, MATCH_GUARD, - PATH_EXPR, RETURN_EXPR, - }, + ast::{self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasName}, + ted, NodeOrToken, + SyntaxKind::{BLOCK_EXPR, BREAK_EXPR, COMMENT, LOOP_EXPR, MATCH_GUARD, PATH_EXPR, RETURN_EXPR}, SyntaxNode, }; @@ -66,98 +62,140 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op .as_ref() .map_or(false, |it| matches!(it, ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_))); - let reference_modifier = match ty.filter(|_| needs_adjust) { - Some(receiver_type) if receiver_type.is_mutable_reference() => "&mut ", - Some(receiver_type) if receiver_type.is_reference() => "&", - _ => "", - }; - - let var_modifier = match parent { - Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => "mut ", - _ => "", - }; - let anchor = Anchor::from(&to_extract)?; - let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone(); let target = to_extract.syntax().text_range(); acc.add( AssistId("extract_variable", AssistKind::RefactorExtract), "Extract into variable", target, move |edit| { - let field_shorthand = - match to_extract.syntax().parent().and_then(ast::RecordExprField::cast) { - Some(field) => field.name_ref(), - None => None, - }; + let field_shorthand = to_extract + .syntax() + .parent() + .and_then(ast::RecordExprField::cast) + .filter(|field| field.name_ref().is_some()); - let mut buf = String::new(); + let (var_name, expr_replace) = match field_shorthand { + Some(field) => (field.to_string(), field.syntax().clone()), + None => ( + suggest_name::for_variable(&to_extract, &ctx.sema), + to_extract.syntax().clone(), + ), + }; - let var_name = match &field_shorthand { - Some(it) => it.to_string(), - None => suggest_name::for_variable(&to_extract, &ctx.sema), + let ident_pat = match parent { + Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => { + make::ident_pat(false, true, make::name(&var_name)) + } + _ => make::ident_pat(false, false, make::name(&var_name)), }; - let expr_range = match &field_shorthand { - Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), - None => to_extract.syntax().text_range(), + + let to_extract = match ty.as_ref().filter(|_| needs_adjust) { + Some(receiver_type) if receiver_type.is_mutable_reference() => { + make::expr_ref(to_extract, true) + } + Some(receiver_type) if receiver_type.is_reference() => { + make::expr_ref(to_extract, false) + } + _ => to_extract, }; + let expr_replace = edit.make_syntax_mut(expr_replace); + let let_stmt = + make::let_stmt(ident_pat.into(), None, Some(to_extract)).clone_for_update(); + let name_expr = make::expr_path(make::ext::ident_path(&var_name)).clone_for_update(); + match anchor { - Anchor::Before(_) | Anchor::Replace(_) => { - format_to!(buf, "let {var_modifier}{var_name} = {reference_modifier}") - } - Anchor::WrapInBlock(_) => { - format_to!(buf, "{{ let {var_name} = {reference_modifier}") - } - }; - format_to!(buf, "{to_extract}"); + Anchor::Before(place) => { + let prev_ws = place.prev_sibling_or_token().and_then(|it| it.into_token()); + let indent_to = IndentLevel::from_node(&place); + let insert_place = edit.make_syntax_mut(place); - if let Anchor::Replace(stmt) = anchor { - cov_mark::hit!(test_extract_var_expr_stmt); - if stmt.semicolon_token().is_none() { - buf.push(';'); - } - match ctx.config.snippet_cap { - Some(cap) => { - let snip = buf.replace( - &format!("let {var_modifier}{var_name}"), - &format!("let {var_modifier}$0{var_name}"), - ); - edit.replace_snippet(cap, expr_range, snip) - } - None => edit.replace(expr_range, buf), - } - return; - } + // Adjust ws to insert depending on if this is all inline or on separate lines + let trailing_ws = if prev_ws.is_some_and(|it| it.text().starts_with("\n")) { + format!("\n{indent_to}") + } else { + format!(" ") + }; - buf.push(';'); - - // We want to maintain the indent level, - // but we do not want to duplicate possible - // extra newlines in the indent block - let text = indent.text(); - if text.starts_with('\n') { - buf.push('\n'); - buf.push_str(text.trim_start_matches('\n')); - } else { - buf.push_str(text); - } - - edit.replace(expr_range, var_name.clone()); - let offset = anchor.syntax().text_range().start(); - match ctx.config.snippet_cap { - Some(cap) => { - let snip = buf.replace( - &format!("let {var_modifier}{var_name}"), - &format!("let {var_modifier}$0{var_name}"), + ted::insert_all_raw( + ted::Position::before(insert_place), + vec![ + let_stmt.syntax().clone().into(), + make::tokens::whitespace(&trailing_ws).into(), + ], ); - edit.insert_snippet(cap, offset, snip) - } - None => edit.insert(offset, buf), - } - if let Anchor::WrapInBlock(_) = anchor { - edit.insert(anchor.syntax().text_range().end(), " }"); + ted::replace(expr_replace, name_expr.syntax()); + + if let Some(cap) = ctx.config.snippet_cap { + if let Some(ast::Pat::IdentPat(ident_pat)) = let_stmt.pat() { + if let Some(name) = ident_pat.name() { + edit.add_tabstop_before(cap, name); + } + } + } + } + Anchor::Replace(stmt) => { + cov_mark::hit!(test_extract_var_expr_stmt); + + let stmt_replace = edit.make_mut(stmt); + ted::replace(stmt_replace.syntax(), let_stmt.syntax()); + + if let Some(cap) = ctx.config.snippet_cap { + if let Some(ast::Pat::IdentPat(ident_pat)) = let_stmt.pat() { + if let Some(name) = ident_pat.name() { + edit.add_tabstop_before(cap, name); + } + } + } + } + Anchor::WrapInBlock(to_wrap) => { + let indent_to = to_wrap.indent_level(); + + let block = if to_wrap.syntax() == &expr_replace { + // Since `expr_replace` is the same that needs to be wrapped in a block, + // we can just directly replace it with a block + let block = + make::block_expr([let_stmt.into()], Some(name_expr)).clone_for_update(); + ted::replace(expr_replace, block.syntax()); + + block + } else { + // `expr_replace` is a descendant of `to_wrap`, so both steps need to be + // handled seperately, otherwise we wrap the wrong expression + let to_wrap = edit.make_mut(to_wrap); + + // Replace the target expr first so that we don't need to find where + // `expr_replace` is in the wrapped `to_wrap` + ted::replace(expr_replace, name_expr.syntax()); + + // Wrap `to_wrap` in a block + let block = make::block_expr([let_stmt.into()], Some(to_wrap.clone())) + .clone_for_update(); + ted::replace(to_wrap.syntax(), block.syntax()); + + block + }; + + if let Some(cap) = ctx.config.snippet_cap { + // Adding a tabstop to `name` requires finding the let stmt again, since + // the existing `let_stmt` is not actually added to the tree + let pat = block.statements().find_map(|stmt| { + let ast::Stmt::LetStmt(let_stmt) = stmt else { return None }; + let_stmt.pat() + }); + + if let Some(ast::Pat::IdentPat(ident_pat)) = pat { + if let Some(name) = ident_pat.name() { + edit.add_tabstop_before(cap, name); + } + } + } + + // fixup indentation of block + block.indent(indent_to); + } } }, ) @@ -181,7 +219,7 @@ fn valid_target_expr(node: SyntaxNode) -> Option { enum Anchor { Before(SyntaxNode), Replace(ast::ExprStmt), - WrapInBlock(SyntaxNode), + WrapInBlock(ast::Expr), } impl Anchor { @@ -204,16 +242,16 @@ impl Anchor { } if let Some(parent) = node.parent() { - if parent.kind() == CLOSURE_EXPR { + if let Some(parent) = ast::ClosureExpr::cast(parent.clone()) { cov_mark::hit!(test_extract_var_in_closure_no_block); - return Some(Anchor::WrapInBlock(node)); + return parent.body().map(Anchor::WrapInBlock); } - if parent.kind() == MATCH_ARM { + if let Some(parent) = ast::MatchArm::cast(parent) { if node.kind() == MATCH_GUARD { cov_mark::hit!(test_extract_var_in_match_guard); } else { cov_mark::hit!(test_extract_var_in_match_arm_no_block); - return Some(Anchor::WrapInBlock(node)); + return parent.expr().map(Anchor::WrapInBlock); } } } @@ -229,13 +267,6 @@ impl Anchor { None }) } - - fn syntax(&self) -> &SyntaxNode { - match self { - Anchor::Before(it) | Anchor::WrapInBlock(it) => it, - Anchor::Replace(stmt) => stmt.syntax(), - } - } } #[cfg(test)] @@ -502,7 +533,10 @@ fn main() { fn main() { let x = true; let tuple = match x { - true => { let $0var_name = 2 + 2; (var_name, true) } + true => { + let $0var_name = 2 + 2; + (var_name, true) + } _ => (0, false) }; } @@ -579,7 +613,10 @@ fn main() { "#, r#" fn main() { - let lambda = |x: u32| { let $0var_name = x * 2; var_name }; + let lambda = |x: u32| { + let $0var_name = x * 2; + var_name + }; } "#, ); diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index f4fa6a74c6b9..0d34502add94 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -2,22 +2,25 @@ use std::ops::Not; use crate::{ assist_context::{AssistContext, Assists}, - utils::convert_param_list_to_arg_list, + utils::{convert_param_list_to_arg_list, suggest_name}, }; use either::Either; use hir::{db::HirDatabase, HasVisibility}; use ide_db::{ assists::{AssistId, GroupLabel}, path_transform::PathTransform, + FxHashMap, FxHashSet, }; +use itertools::Itertools; use syntax::{ ast::{ self, edit::{self, AstNodeEdit}, - make, AssocItem, HasGenericParams, HasName, HasVisibility as astHasVisibility, Path, + make, AssocItem, GenericArgList, GenericParamList, HasGenericParams, HasName, + HasTypeBounds, HasVisibility as astHasVisibility, Path, }, ted::{self, Position}, - AstNode, NodeOrToken, SyntaxKind, + AstNode, NodeOrToken, SmolStr, SyntaxKind, }; // Assist: generate_delegate_trait @@ -77,7 +80,7 @@ use syntax::{ // } // // fn method_(&mut self) -> bool { -// ::method_( &mut self.a ) +// ::method_(&mut self.a) // } // } // ``` @@ -98,6 +101,7 @@ pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_> } /// A utility object that represents a struct's field. +#[derive(Debug)] struct Field { name: String, ty: ast::Type, @@ -111,44 +115,33 @@ impl Field { f: Either, ) -> Option { let db = ctx.sema.db; - let name: String; - let range: syntax::TextRange; - let ty: ast::Type; let module = ctx.sema.to_module_def(ctx.file_id())?; - match f { + let (name, range, ty) = match f { Either::Left(f) => { - name = f.name()?.to_string(); - ty = f.ty()?; - range = f.syntax().text_range(); + let name = f.name()?.to_string(); + (name, f.syntax().text_range(), f.ty()?) } Either::Right((f, l)) => { - name = l.fields().position(|it| it == f)?.to_string(); - ty = f.ty()?; - range = f.syntax().text_range(); + let name = l.fields().position(|it| it == f)?.to_string(); + (name, f.syntax().text_range(), f.ty()?) } }; let hir_ty = ctx.sema.resolve_type(&ty)?; let type_impls = hir::Impl::all_for_type(db, hir_ty.clone()); let mut impls = Vec::with_capacity(type_impls.len()); - let type_param = hir_ty.as_type_param(db); - if let Some(tp) = type_param { + if let Some(tp) = hir_ty.as_type_param(db) { for tb in tp.trait_bounds(db) { - impls.push(Delegee::Bound(BoundCase(tb))); + impls.push(Delegee::Bound(tb)); } }; for imp in type_impls { - match imp.trait_(db) { - Some(tr) => { - if tr.is_visible_from(db, module) { - impls.push(Delegee::Impls(ImplCase(tr, imp))) - } - } - None => (), + if let Some(tr) = imp.trait_(db).filter(|tr| tr.is_visible_from(db, module)) { + impls.push(Delegee::Impls(tr, imp)) } } @@ -161,19 +154,17 @@ impl Field { /// actually implements the trait and the second way is when the field /// has a bound type parameter. We handle these cases in different ways /// hence the enum. +#[derive(Debug)] enum Delegee { - Bound(BoundCase), - Impls(ImplCase), + Bound(hir::Trait), + Impls(hir::Trait, hir::Impl), } -struct BoundCase(hir::Trait); -struct ImplCase(hir::Trait, hir::Impl); - impl Delegee { fn signature(&self, db: &dyn HirDatabase) -> String { let mut s = String::new(); - let (Delegee::Bound(BoundCase(it)) | Delegee::Impls(ImplCase(it, _))) = self; + let (Delegee::Bound(it) | Delegee::Impls(it, _)) = self; for m in it.module(db).path_to_root(db).iter().rev() { if let Some(name) = m.name(db) { @@ -200,25 +191,33 @@ impl Struct { pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) { let db = ctx.db(); + for delegee in &field.impls { + let trait_ = match delegee { + Delegee::Bound(b) => b, + Delegee::Impls(i, _) => i, + }; + + // Skip trait that has `Self` type, which cannot be delegated + // + // See [`test_self_ty`] + if has_self_type(*trait_, ctx).is_some() { + continue; + } + // FIXME : We can omit already implemented impl_traits // But we don't know what the &[hir::Type] argument should look like. - - // let trait_ = match delegee { - // Delegee::Bound(b) => b.0, - // Delegee::Impls(i) => i.1, - // }; - // if self.hir_ty.impls_trait(db, trait_, &[]) { // continue; // } let signature = delegee.signature(db); + let Some(delegate) = generate_impl(ctx, self, &field.ty, &field.name, delegee) else { continue; }; acc.add_group( - &GroupLabel("Delegate trait impl for field...".to_owned()), + &GroupLabel(format!("Generate delegate impls for field `{}`", field.name)), AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate), format!("Generate delegate impl `{}` for `{}`", signature, field.name), field.range, @@ -241,46 +240,40 @@ fn generate_impl( delegee: &Delegee, ) -> Option { let delegate: ast::Impl; - let source: ast::Impl; - let genpar: Option; let db = ctx.db(); - let base_path = make::path_from_text(&field_ty.to_string().as_str()); - let s_path = make::ext::ident_path(&strukt.name.to_string()); + let ast_strukt = &strukt.strukt; + let strukt_ty = make::ty_path(make::ext::ident_path(&strukt.name.to_string())); match delegee { Delegee::Bound(delegee) => { - let in_file = ctx.sema.source(delegee.0.to_owned())?; - let source: ast::Trait = in_file.value; + let bound_def = ctx.sema.source(delegee.to_owned())?.value; + let bound_params = bound_def.generic_param_list(); + let strukt_params = ast_strukt.generic_param_list(); delegate = make::impl_trait( - delegee.0.is_unsafe(db), - None, - None, - strukt.strukt.generic_param_list(), - None, - delegee.0.is_auto(db), - make::ty(&delegee.0.name(db).to_smol_str()), - make::ty_path(s_path), - source.where_clause(), - strukt.strukt.where_clause(), + delegee.is_unsafe(db), + bound_params.clone(), + bound_params.map(|params| params.to_generic_args()), + strukt_params.clone(), + strukt_params.map(|params| params.to_generic_args()), + delegee.is_auto(db), + make::ty(&delegee.name(db).to_smol_str()), + strukt_ty, + bound_def.where_clause(), + ast_strukt.where_clause(), None, ) .clone_for_update(); - genpar = source.generic_param_list(); - let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); - let gen_args: String = - genpar.map_or_else(String::new, |params| params.to_generic_args().to_string()); - // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths let qualified_path_type = make::path_from_text(&format!( - "<{} as {}{}>", - base_path.to_string(), - delegee.0.name(db).to_smol_str(), - gen_args.to_string() + "<{} as {}>", + field_ty.to_string(), + delegate.trait_()?.to_string() )); - match source.assoc_item_list() { + let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); + match bound_def.assoc_item_list() { Some(ai) => { ai.assoc_items() .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) @@ -295,66 +288,394 @@ fn generate_impl( None => {} }; - let target = ctx.sema.scope(strukt.strukt.syntax())?; - let source = ctx.sema.scope(source.syntax())?; - - let transform = - PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); + let target_scope = ctx.sema.scope(strukt.strukt.syntax())?; + let source_scope = ctx.sema.scope(bound_def.syntax())?; + let transform = PathTransform::generic_transformation(&target_scope, &source_scope); transform.apply(&delegate.syntax()); } - Delegee::Impls(delegee) => { - let in_file = ctx.sema.source(delegee.1.to_owned())?; - source = in_file.value; + Delegee::Impls(trait_, old_impl) => { + let old_impl = ctx.sema.source(old_impl.to_owned())?.value; + + // `old_trait_args` contains names of generic args for trait in `old_impl` + let old_trait_args = old_impl + .trait_()? + .generic_arg_list() + .map(|l| l.generic_args().map(|arg| arg.to_string())) + .map_or_else(|| FxHashSet::default(), |it| it.collect()); + + let old_impl_params = old_impl.generic_param_list(); + + // Resolve conflicts with generic parameters in strukt. + // These generics parameters will also be used in `field_ty` and `where_clauses`, + // so we should substitude arguments in them as well. + let (renamed_strukt_params, field_ty, ty_where_clause) = if let Some(strukt_params) = + resolve_conflicts_for_strukt(ast_strukt, old_impl_params.as_ref()) + { + let strukt_args = strukt_params.to_generic_args(); + let field_ty = + subst_name_in_strukt(ctx, ast_strukt, field_ty, strukt_args.clone())?; + let wc = ast_strukt + .where_clause() + .and_then(|wc| Some(subst_name_in_strukt(ctx, ast_strukt, &wc, strukt_args)?)); + (Some(strukt_params), field_ty, wc) + } else { + (None, field_ty.clone_for_update(), None) + }; + + // Some generics used in `field_ty` may be instantiated, so they are no longer + // `generics`. We should remove them from generics params, and use the rest params. + let trait_gen_params = + remove_instantiated_params(&old_impl.self_ty()?, old_impl_params, &old_trait_args); + + // Generate generic args that applied to current impl, this step will also remove unused params + let args_for_impl = + get_args_for_impl(&old_impl, &field_ty, &trait_gen_params, &old_trait_args); + + let mut trait_gen_args = old_impl.trait_()?.generic_arg_list(); + if let Some(arg_list) = &mut trait_gen_args { + *arg_list = arg_list.clone_for_update(); + transform_impl(ctx, ast_strukt, &old_impl, &args_for_impl, &arg_list.syntax())?; + } + + let mut type_gen_args = + renamed_strukt_params.clone().map(|params| params.to_generic_args()); + if let Some(type_args) = &mut type_gen_args { + *type_args = type_args.clone_for_update(); + transform_impl(ctx, ast_strukt, &old_impl, &args_for_impl, &type_args.syntax())?; + } + + let path_type = make::ty(&trait_.name(db).to_smol_str()).clone_for_update(); + transform_impl(ctx, ast_strukt, &old_impl, &args_for_impl, &path_type.syntax())?; + delegate = make::impl_trait( - delegee.0.is_unsafe(db), - source.generic_param_list(), - None, - None, - None, - delegee.0.is_auto(db), - make::ty(&delegee.0.name(db).to_smol_str()), - make::ty_path(s_path), - source.where_clause(), - strukt.strukt.where_clause(), + trait_.is_unsafe(db), + trait_gen_params, + trait_gen_args, + renamed_strukt_params, + type_gen_args, + trait_.is_auto(db), + path_type, + strukt_ty, + old_impl.where_clause().map(|wc| wc.clone_for_update()), + ty_where_clause, None, ) .clone_for_update(); - genpar = source.generic_param_list(); - let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); - let gen_args: String = - genpar.map_or_else(String::new, |params| params.to_generic_args().to_string()); // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths let qualified_path_type = make::path_from_text(&format!( - "<{} as {}{}>", - base_path.to_string().as_str(), - delegee.0.name(db).to_smol_str(), - gen_args.to_string().as_str() + "<{} as {}>", + field_ty.to_string(), + delegate.trait_()?.to_string() )); - source + let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); + for item in old_impl .get_or_create_assoc_item_list() .assoc_items() .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) - .for_each(|item| { - let assoc = process_assoc_item(item, qualified_path_type.clone(), &field_name); - if let Some(assoc) = assoc { - delegate_assoc_items.add_item(assoc); - } - }); + { + let assoc = process_assoc_item( + transform_assoc_item(ctx, ast_strukt, &old_impl, &args_for_impl, item)?, + qualified_path_type.clone(), + &field_name, + )?; - let target = ctx.sema.scope(strukt.strukt.syntax())?; - let source = ctx.sema.scope(source.syntax())?; + delegate_assoc_items.add_item(assoc); + } - let transform = - PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); - transform.apply(&delegate.syntax()); + // Remove unused where clauses + if let Some(wc) = delegate.where_clause() { + remove_useless_where_clauses(&delegate, wc)?; + } } } Some(delegate) } +fn transform_assoc_item( + ctx: &AssistContext<'_>, + strukt: &ast::Struct, + old_impl: &ast::Impl, + args: &Option, + item: AssocItem, +) -> Option { + let source_scope = ctx.sema.scope(&item.syntax()).unwrap(); + let target_scope = ctx.sema.scope(&strukt.syntax())?; + let hir_old_impl = ctx.sema.to_impl_def(old_impl)?; + let item = item.clone_for_update(); + let transform = args.as_ref().map_or_else( + || PathTransform::generic_transformation(&target_scope, &source_scope), + |args| { + PathTransform::impl_transformation( + &target_scope, + &source_scope, + hir_old_impl, + args.clone(), + ) + }, + ); + transform.apply(&item.syntax()); + Some(item) +} + +fn transform_impl( + ctx: &AssistContext<'_>, + strukt: &ast::Struct, + old_impl: &ast::Impl, + args: &Option, + syntax: &syntax::SyntaxNode, +) -> Option<()> { + let source_scope = ctx.sema.scope(&old_impl.self_ty()?.syntax())?; + let target_scope = ctx.sema.scope(&strukt.syntax())?; + let hir_old_impl = ctx.sema.to_impl_def(old_impl)?; + + let transform = args.as_ref().map_or_else( + || PathTransform::generic_transformation(&target_scope, &source_scope), + |args| { + PathTransform::impl_transformation( + &target_scope, + &source_scope, + hir_old_impl, + args.clone(), + ) + }, + ); + + transform.apply(&syntax); + Some(()) +} + +fn remove_instantiated_params( + self_ty: &ast::Type, + old_impl_params: Option, + old_trait_args: &FxHashSet, +) -> Option { + match self_ty { + ast::Type::PathType(path_type) => { + old_impl_params.and_then(|gpl| { + // Remove generic parameters in field_ty (which is instantiated). + let new_gpl = gpl.clone_for_update(); + + path_type + .path()? + .segments() + .filter_map(|seg| seg.generic_arg_list()) + .flat_map(|it| it.generic_args()) + // However, if the param is also used in the trait arguments, it shouldn't be removed. + .filter(|arg| !old_trait_args.contains(&arg.to_string())) + .for_each(|arg| { + new_gpl.remove_generic_arg(&arg); + }); + (new_gpl.generic_params().count() > 0).then_some(new_gpl) + }) + } + _ => old_impl_params, + } +} + +fn remove_useless_where_clauses(delegate: &ast::Impl, wc: ast::WhereClause) -> Option<()> { + let trait_args = + delegate.trait_()?.generic_arg_list().map(|trait_args| trait_args.generic_args()); + let strukt_args = + delegate.self_ty()?.generic_arg_list().map(|strukt_args| strukt_args.generic_args()); + let used_generic_names = match (trait_args, strukt_args) { + (None, None) => None, + (None, Some(y)) => Some(y.map(|arg| arg.to_string()).collect::>()), + (Some(x), None) => Some(x.map(|arg| arg.to_string()).collect::>()), + (Some(x), Some(y)) => Some(x.chain(y).map(|arg| arg.to_string()).collect::>()), + }; + + // Keep clauses that have generic clauses after substitution, and remove the rest + if let Some(used_generic_names) = used_generic_names { + wc.predicates() + .filter(|pred| { + pred.syntax() + .descendants_with_tokens() + .filter_map(|e| e.into_token()) + .find(|e| { + e.kind() == SyntaxKind::IDENT && used_generic_names.contains(&e.to_string()) + }) + .is_none() + }) + .for_each(|pred| { + wc.remove_predicate(pred); + }); + } else { + wc.predicates().for_each(|pred| wc.remove_predicate(pred)); + } + + if wc.predicates().count() == 0 { + // Remove useless whitespaces + wc.syntax() + .siblings_with_tokens(syntax::Direction::Prev) + .skip(1) + .take_while(|node_or_tok| node_or_tok.kind() == SyntaxKind::WHITESPACE) + .for_each(|ws| ted::remove(ws)); + wc.syntax() + .siblings_with_tokens(syntax::Direction::Next) + .skip(1) + .take_while(|node_or_tok| node_or_tok.kind() == SyntaxKind::WHITESPACE) + .for_each(|ws| ted::remove(ws)); + ted::insert( + ted::Position::after(wc.syntax()), + NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), + ); + // Remove where clause + ted::remove(wc.syntax()); + } + + Some(()) +} + +fn get_args_for_impl( + old_impl: &ast::Impl, + field_ty: &ast::Type, + trait_params: &Option, + old_trait_args: &FxHashSet, +) -> Option { + // Generate generic args that should be apply to current impl + // + // For exmaple, if we have `impl Trait for B`, and `b: B` in `S`, + // then the generic `A` should be renamed to `T`. While the last two generic args + // doesn't change, it renames . So we apply `` as generic arguments + // to impl. + let old_impl_params = old_impl.generic_param_list(); + let self_ty = old_impl.self_ty(); + + if let (Some(old_impl_gpl), Some(self_ty)) = (old_impl_params, self_ty) { + // Make pair of the arguments of `field_ty` and `old_strukt_args` to + // get the list for substitution + let mut arg_substs = FxHashMap::default(); + + match field_ty { + field_ty @ ast::Type::PathType(_) => { + let field_args = field_ty.generic_arg_list(); + if let (Some(field_args), Some(old_impl_args)) = + (field_args, self_ty.generic_arg_list()) + { + field_args.generic_args().zip(old_impl_args.generic_args()).for_each( + |(field_arg, impl_arg)| { + arg_substs.entry(impl_arg.to_string()).or_insert(field_arg); + }, + ) + } + } + _ => {} + } + + let args = old_impl_gpl + .to_generic_args() + .generic_args() + .map(|old_arg| { + arg_substs.get(&old_arg.to_string()).map_or_else( + || old_arg.clone(), + |replace_with| { + // The old_arg will be replaced, so it becomes redundant + let old_arg_name = old_arg.to_string(); + if old_trait_args.contains(&old_arg_name) { + // However, we should check type bounds and where clauses on old_arg, + // if it has type bound, we should keep the type bound. + // match trait_params.and_then(|params| params.remove_generic_arg(&old_arg)) { + // Some(ast::GenericParam::TypeParam(ty)) => { + // ty.type_bound_list().and_then(|bounds| ) + // } + // _ => {} + // } + if let Some(params) = trait_params { + params.remove_generic_arg(&old_arg); + } + } + replace_with.clone() + }, + ) + }) + .collect_vec(); + args.is_empty().not().then(|| make::generic_arg_list(args.into_iter())) + } else { + None + } +} + +fn subst_name_in_strukt( + ctx: &AssistContext<'_>, + strukt: &ast::Struct, + item: &N, + args: GenericArgList, +) -> Option +where + N: ast::AstNode, +{ + let hir_strukt = ctx.sema.to_struct_def(strukt)?; + let hir_adt = hir::Adt::from(hir_strukt); + + let item = item.clone_for_update(); + let item_scope = ctx.sema.scope(item.syntax())?; + let transform = PathTransform::adt_transformation(&item_scope, &item_scope, hir_adt, args); + transform.apply(&item.syntax()); + Some(item) +} + +fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> Option<()> { + let trait_source = ctx.sema.source(trait_)?.value; + trait_source + .syntax() + .descendants_with_tokens() + .filter_map(|e| e.into_token()) + .find(|e| e.kind() == SyntaxKind::SELF_TYPE_KW) + .map(|_| ()) +} + +fn resolve_conflicts_for_strukt( + strukt: &ast::Struct, + old_impl_params: Option<&ast::GenericParamList>, +) -> Option { + match (strukt.generic_param_list(), old_impl_params) { + (Some(old_strukt_params), Some(old_impl_params)) => { + let params = make::generic_param_list(std::iter::empty()).clone_for_update(); + + for old_strukt_param in old_strukt_params.generic_params() { + // Get old name from `strukt`` + let mut name = SmolStr::from(match &old_strukt_param { + ast::GenericParam::ConstParam(c) => c.name()?.to_string(), + ast::GenericParam::LifetimeParam(l) => { + l.lifetime()?.lifetime_ident_token()?.to_string() + } + ast::GenericParam::TypeParam(t) => t.name()?.to_string(), + }); + + // The new name cannot be conflicted with generics in trait, and the renamed names. + name = suggest_name::for_unique_generic_name(&name, old_impl_params); + name = suggest_name::for_unique_generic_name(&name, ¶ms); + match old_strukt_param { + ast::GenericParam::ConstParam(c) => { + if let Some(const_ty) = c.ty() { + let const_param = make::const_param(make::name(&name), const_ty); + params.add_generic_param(ast::GenericParam::ConstParam( + const_param.clone_for_update(), + )); + } + } + p @ ast::GenericParam::LifetimeParam(_) => { + params.add_generic_param(p.clone_for_update()); + } + ast::GenericParam::TypeParam(t) => { + let type_bounds = t.type_bound_list(); + let type_param = make::type_param(make::name(&name), type_bounds); + params.add_generic_param(ast::GenericParam::TypeParam( + type_param.clone_for_update(), + )); + } + } + } + Some(params) + } + (Some(old_strukt_gpl), None) => Some(old_strukt_gpl), + _ => None, + } +} + fn process_assoc_item( item: syntax::ast::AssocItem, qual_path_ty: ast::Path, @@ -381,10 +702,14 @@ fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option // >::ConstName; // FIXME : We can't rely on `make::path_qualified` for now but it would be nice to replace the following with it. // make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap()); - let qualpath = qualpath(qual_path_ty, path_expr_segment); - let inner = - make::item_const(item.visibility(), item.name()?, item.ty()?, make::expr_path(qualpath)) - .clone_for_update(); + let qualified_path = qualified_path(qual_path_ty, path_expr_segment); + let inner = make::item_const( + item.visibility(), + item.name()?, + item.ty()?, + make::expr_path(qualified_path), + ) + .clone_for_update(); Some(AssocItem::Const(inner)) } @@ -395,7 +720,7 @@ fn func_assoc_item( base_name: &str, ) -> Option { let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str()); - let qualpath = qualpath(qual_path_ty, path_expr_segment); + let qualified_path = qualified_path(qual_path_ty, path_expr_segment); let call = match item.param_list() { // Methods and funcs should be handled separately. @@ -413,31 +738,33 @@ fn func_assoc_item( let param_count = l.params().count(); let args = convert_param_list_to_arg_list(l).clone_for_update(); - + let pos_after_l_paren = Position::after(args.l_paren_token()?); if param_count > 0 { // Add SelfParam and a TOKEN::COMMA - ted::insert_all( - Position::after(args.l_paren_token()?), + ted::insert_all_raw( + pos_after_l_paren, vec![ NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), - NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), NodeOrToken::Token(make::token(SyntaxKind::COMMA)), + NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), ], ); } else { // Add SelfParam only - ted::insert( - Position::after(args.l_paren_token()?), + ted::insert_raw( + pos_after_l_paren, NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), ); } - make::expr_call(make::expr_path(qualpath), args) + make::expr_call(make::expr_path(qualified_path), args) + } + None => { + make::expr_call(make::expr_path(qualified_path), convert_param_list_to_arg_list(l)) } - None => make::expr_call(make::expr_path(qualpath), convert_param_list_to_arg_list(l)), }, None => make::expr_call( - make::expr_path(qualpath), + make::expr_path(qualified_path), convert_param_list_to_arg_list(make::param_list(None, Vec::new())), ), } @@ -463,8 +790,8 @@ fn func_assoc_item( fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option { let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str()); - let qualpath = qualpath(qual_path_ty, path_expr_segment); - let ty = make::ty_path(qualpath); + let qualified_path = qualified_path(qual_path_ty, path_expr_segment); + let ty = make::ty_path(qualified_path); let ident = item.name()?.to_string(); let alias = make::ty_alias( @@ -479,7 +806,7 @@ fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option ast::Path { +fn qualified_path(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path { make::path_from_text(&format!("{}::{}", qual_path_ty.to_string(), path_expr_seg.to_string())) } @@ -510,6 +837,29 @@ impl Trait for Base {} ); } + #[test] + fn test_self_ty() { + // trait whith `Self` type cannot be delegated + // + // See the function `fn f() -> Self`. + // It should be `fn f() -> Base` in `Base`, and `fn f() -> S` in `S` + check_assist_not_applicable( + generate_delegate_trait, + r#" +struct Base(()); +struct S(B$0ase); +trait Trait { + fn f() -> Self; +} +impl Trait for Base { + fn f() -> Base { + Base(()) + } +} +"#, + ); + } + #[test] fn test_struct_struct_basic() { check_assist( @@ -628,7 +978,7 @@ unsafe impl Trait for S { } unsafe fn a_method(&self) { - ::a_method( &self.base ) + ::a_method(&self.base) } } @@ -672,6 +1022,245 @@ where ); } + #[test] + fn test_fields_with_generics() { + check_assist( + generate_delegate_trait, + r#" +struct B { + a: T +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T1) -> T1 { a } +} + +struct A {} +struct S { + b :$0 B, +} +"#, + r#" +struct B { + a: T +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T1) -> T1 { a } +} + +struct A {} +struct S { + b : B, +} + +impl Trait for S { + fn f(&self, a: T1) -> T1 { + as Trait>::f(&self.b, a) + } +} +"#, + ); + } + + #[test] + fn test_generics_with_conflict_names() { + check_assist( + generate_delegate_trait, + r#" +struct B { + a: T +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S { + b : $0B, +} +"#, + r#" +struct B { + a: T +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S { + b : B, +} + +impl Trait for S { + fn f(&self, a: T) -> T { + as Trait>::f(&self.b, a) + } +} +"#, + ); + } + + #[test] + fn test_lifetime_with_conflict_names() { + check_assist( + generate_delegate_trait, + r#" +struct B<'a, T> { + a: &'a T +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl<'a, T, T0> Trait for B<'a, T0> { + fn f(&self, a: T) -> T { a } +} + +struct S<'a, T> { + b : $0B<'a, T>, +} +"#, + r#" +struct B<'a, T> { + a: &'a T +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl<'a, T, T0> Trait for B<'a, T0> { + fn f(&self, a: T) -> T { a } +} + +struct S<'a, T> { + b : B<'a, T>, +} + +impl<'a, T, T1> Trait for S<'a, T1> { + fn f(&self, a: T) -> T { + as Trait>::f(&self.b, a) + } +} +"#, + ); + } + + #[test] + fn test_multiple_generics() { + check_assist( + generate_delegate_trait, + r#" +struct B { + a: T1, + b: T2 +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S { + b :$0 B, +} +"#, + r#" +struct B { + a: T1, + b: T2 +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S { + b : B, +} + +impl Trait for S { + fn f(&self, a: i32) -> i32 { + as Trait>::f(&self.b, a) + } +} +"#, + ); + } + + #[test] + fn test_generics_multiplex() { + check_assist( + generate_delegate_trait, + r#" +struct B { + a: T +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S { + b : $0B, +} +"#, + r#" +struct B { + a: T +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S { + b : B, +} + +impl Trait for S { + fn f(&self, a: T0) -> T0 { + as Trait>::f(&self.b, a) + } +} +"#, + ); + } + #[test] fn test_complex_without_where() { check_assist( @@ -719,7 +1308,7 @@ impl<'a, T, const C: usize> Trait<'a, T, C> for S { } fn assoc_method(&self, p: ()) { - >::assoc_method( &self.field , p) + >::assoc_method(&self.field, p) } } @@ -789,7 +1378,7 @@ where } fn assoc_method(&self, p: ()) { - >::assoc_method( &self.field , p) + >::assoc_method(&self.field, p) } } @@ -875,7 +1464,7 @@ where } fn assoc_method(&self, p: ()) { - >::assoc_method( &self.field , p) + >::assoc_method(&self.field, p) } } @@ -923,6 +1512,132 @@ where ); } + #[test] + fn test_type_bound_with_generics_1() { + check_assist( + generate_delegate_trait, + r#" +trait AnotherTrait {} +struct B +where + T1: AnotherTrait +{ + a: T, + b: T1 +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S +where + T1: AnotherTrait +{ + b : $0B, +}"#, + r#" +trait AnotherTrait {} +struct B +where + T1: AnotherTrait +{ + a: T, + b: T1 +} + +trait Trait { + fn f(&self, a: T) -> T; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S +where + T1: AnotherTrait +{ + b : B, +} + +impl Trait for S +where + T10: AnotherTrait +{ + fn f(&self, a: T) -> T { + as Trait>::f(&self.b, a) + } +}"#, + ); + } + + #[test] + fn test_type_bound_with_generics_2() { + check_assist( + generate_delegate_trait, + r#" +trait AnotherTrait {} +struct B +where + T1: AnotherTrait +{ + b: T1 +} + +trait Trait { + fn f(&self, a: T1) -> T1; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S +where + T: AnotherTrait +{ + b : $0B, +}"#, + r#" +trait AnotherTrait {} +struct B +where + T1: AnotherTrait +{ + b: T1 +} + +trait Trait { + fn f(&self, a: T1) -> T1; +} + +impl Trait for B { + fn f(&self, a: T) -> T { a } +} + +struct S +where + T: AnotherTrait +{ + b : B, +} + +impl Trait for S +where + T0: AnotherTrait +{ + fn f(&self, a: T) -> T { + as Trait>::f(&self.b, a) + } +}"#, + ); + } + #[test] fn test_docstring_example() { check_assist( @@ -975,7 +1690,7 @@ impl SomeTrait for B { } fn method_(&mut self) -> bool { - ::method_( &mut self.a ) + ::method_(&mut self.a) } } "#, @@ -1043,7 +1758,7 @@ impl some_module::SomeTrait for B { } fn method_(&mut self) -> bool { - ::method_( &mut self.a ) + ::method_(&mut self.a) } }"#, ) diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs index 1a1e992e28a4..2aaf9d0679d3 100644 --- a/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -114,7 +114,7 @@ fn add_variant_to_accumulator( parent: PathParent, ) -> Option<()> { let db = ctx.db(); - let InRealFile { file_id, value: enum_node } = adt.source(db)?.original_ast_node(db)?; + let InRealFile { file_id, value: enum_node } = adt.source(db)?.original_ast_node_rooted(db)?; acc.add( AssistId("generate_enum_variant", AssistKind::Generate), diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index a113c817f7e9..5bb200e84a49 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -8,20 +8,21 @@ use ide_db::{ famous_defs::FamousDefs, helpers::is_editable_crate, path_transform::PathTransform, + source_change::SourceChangeBuilder, FxHashMap, FxHashSet, RootDatabase, SnippetCap, }; +use itertools::Itertools; use stdx::to_lower_snake_case; use syntax::{ ast::{ - self, - edit::{AstNodeEdit, IndentLevel}, - make, AstNode, CallExpr, HasArgList, HasGenericParams, HasModuleItem, HasTypeBounds, + self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, CallExpr, HasArgList, + HasGenericParams, HasModuleItem, HasTypeBounds, }, - SyntaxKind, SyntaxNode, TextRange, TextSize, + ted, SyntaxKind, SyntaxNode, TextRange, T, }; use crate::{ - utils::{convert_reference_type, find_struct_impl, render_snippet, Cursor}, + utils::{convert_reference_type, find_struct_impl}, AssistContext, AssistId, AssistKind, Assists, }; @@ -65,7 +66,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } let fn_name = &*name_ref.text(); - let TargetInfo { target_module, adt_name, target, file, insert_offset } = + let TargetInfo { target_module, adt_name, target, file } = fn_target_info(ctx, path, &call, fn_name)?; if let Some(m) = target_module { @@ -77,16 +78,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; let text_range = call.syntax().text_range(); let label = format!("Generate {} function", function_builder.fn_name); - add_func_to_accumulator( - acc, - ctx, - text_range, - function_builder, - insert_offset, - file, - adt_name, - label, - ) + add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_name, label) } struct TargetInfo { @@ -94,7 +86,6 @@ struct TargetInfo { adt_name: Option, target: GeneratedFunctionTarget, file: FileId, - insert_offset: TextSize, } impl TargetInfo { @@ -103,9 +94,8 @@ impl TargetInfo { adt_name: Option, target: GeneratedFunctionTarget, file: FileId, - insert_offset: TextSize, ) -> Self { - Self { target_module, adt_name, target, file, insert_offset } + Self { target_module, adt_name, target, file } } } @@ -156,7 +146,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?; - let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?; + let target = get_method_target(ctx, &impl_, &adt)?; let function_builder = FunctionBuilder::from_method_call( ctx, @@ -169,16 +159,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let text_range = call.syntax().text_range(); let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; let label = format!("Generate {} method", function_builder.fn_name); - add_func_to_accumulator( - acc, - ctx, - text_range, - function_builder, - insert_offset, - file, - adt_name, - label, - ) + add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_name, label) } fn add_func_to_accumulator( @@ -186,23 +167,28 @@ fn add_func_to_accumulator( ctx: &AssistContext<'_>, text_range: TextRange, function_builder: FunctionBuilder, - insert_offset: TextSize, file: FileId, adt_name: Option, label: String, ) -> Option<()> { - acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |builder| { - let indent = IndentLevel::from_node(function_builder.target.syntax()); - let function_template = function_builder.render(adt_name.is_some()); - let mut func = function_template.to_string(ctx.config.snippet_cap); + acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |edit| { + edit.edit_file(file); + + let target = function_builder.target.clone(); + let function_template = function_builder.render(); + let func = function_template.to_ast(ctx.config.snippet_cap, edit); + if let Some(name) = adt_name { + let name = make::ty_path(make::ext::ident_path(&format!("{}", name.display(ctx.db())))); + // FIXME: adt may have generic params. - func = format!("\n{indent}impl {} {{\n{func}\n{indent}}}", name.display(ctx.db())); - } - builder.edit_file(file); - match ctx.config.snippet_cap { - Some(cap) => builder.insert_snippet(cap, insert_offset, func), - None => builder.insert(insert_offset, func), + let impl_ = make::impl_(None, None, name, None, None).clone_for_update(); + + func.indent(IndentLevel(1)); + impl_.get_or_create_assoc_item_list().add_item(func.into()); + target.insert_impl_at(edit, impl_); + } else { + target.insert_fn_at(edit, func); } }) } @@ -220,36 +206,33 @@ fn get_adt_source( } struct FunctionTemplate { - leading_ws: String, fn_def: ast::Fn, ret_type: Option, should_focus_return_type: bool, - trailing_ws: String, tail_expr: ast::Expr, } impl FunctionTemplate { - fn to_string(&self, cap: Option) -> String { - let Self { leading_ws, fn_def, ret_type, should_focus_return_type, trailing_ws, tail_expr } = - self; + fn to_ast(&self, cap: Option, edit: &mut SourceChangeBuilder) -> ast::Fn { + let Self { fn_def, ret_type, should_focus_return_type, tail_expr } = self; - let f = match cap { - Some(cap) => { - let cursor = if *should_focus_return_type { - // Focus the return type if there is one - match ret_type { - Some(ret_type) => ret_type.syntax(), - None => tail_expr.syntax(), + if let Some(cap) = cap { + if *should_focus_return_type { + // Focus the return type if there is one + match ret_type { + Some(ret_type) => { + edit.add_placeholder_snippet(cap, ret_type.clone()); } - } else { - tail_expr.syntax() - }; - render_snippet(cap, fn_def.syntax(), Cursor::Replace(cursor)) + None => { + edit.add_placeholder_snippet(cap, tail_expr.clone()); + } + } + } else { + edit.add_placeholder_snippet(cap, tail_expr.clone()); } - None => fn_def.to_string(), - }; + } - format!("{leading_ws}{f}{trailing_ws}") + fn_def.clone() } } @@ -356,7 +339,7 @@ impl FunctionBuilder { }) } - fn render(self, is_method: bool) -> FunctionTemplate { + fn render(self) -> FunctionTemplate { let placeholder_expr = make::ext::expr_todo(); let fn_body = make::block_expr(vec![], Some(placeholder_expr)); let visibility = match self.visibility { @@ -364,7 +347,7 @@ impl FunctionBuilder { Visibility::Crate => Some(make::visibility_pub_crate()), Visibility::Pub => Some(make::visibility_pub()), }; - let mut fn_def = make::fn_( + let fn_def = make::fn_( visibility, self.fn_name, self.generic_param_list, @@ -375,34 +358,10 @@ impl FunctionBuilder { self.is_async, false, // FIXME : const and unsafe are not handled yet. false, - ); - let leading_ws; - let trailing_ws; - - match self.target { - GeneratedFunctionTarget::BehindItem(it) => { - let mut indent = IndentLevel::from_node(&it); - if is_method { - indent = indent + 1; - leading_ws = format!("{indent}"); - } else { - leading_ws = format!("\n\n{indent}"); - } - - fn_def = fn_def.indent(indent); - trailing_ws = String::new(); - } - GeneratedFunctionTarget::InEmptyItemList(it) => { - let indent = IndentLevel::from_node(&it); - let leading_indent = indent + 1; - leading_ws = format!("\n{leading_indent}"); - fn_def = fn_def.indent(leading_indent); - trailing_ws = format!("\n{indent}"); - } - }; + ) + .clone_for_update(); FunctionTemplate { - leading_ws, ret_type: fn_def.ret_type(), // PANIC: we guarantee we always create a function body with a tail expr tail_expr: fn_def @@ -412,7 +371,6 @@ impl FunctionBuilder { .expect("function body should have a tail expression"), should_focus_return_type: self.should_focus_return_type, fn_def, - trailing_ws, } } } @@ -456,40 +414,37 @@ fn get_fn_target_info( target_module: Option, call: CallExpr, ) -> Option { - let (target, file, insert_offset) = get_fn_target(ctx, target_module, call)?; - Some(TargetInfo::new(target_module, None, target, file, insert_offset)) + let (target, file) = get_fn_target(ctx, target_module, call)?; + Some(TargetInfo::new(target_module, None, target, file)) } fn get_fn_target( ctx: &AssistContext<'_>, target_module: Option, call: CallExpr, -) -> Option<(GeneratedFunctionTarget, FileId, TextSize)> { +) -> Option<(GeneratedFunctionTarget, FileId)> { let mut file = ctx.file_id(); let target = match target_module { Some(target_module) => { - let module_source = target_module.definition_source(ctx.db()); - let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; + let (in_file, target) = next_space_for_fn_in_module(ctx.db(), target_module); file = in_file; target } None => next_space_for_fn_after_call_site(ast::CallableExpr::Call(call))?, }; - Some((target.clone(), file, get_insert_offset(&target))) + Some((target.clone(), file)) } fn get_method_target( ctx: &AssistContext<'_>, impl_: &Option, adt: &Adt, -) -> Option<(GeneratedFunctionTarget, TextSize)> { +) -> Option { let target = match impl_ { - Some(impl_) => next_space_for_fn_in_impl(impl_)?, - None => { - GeneratedFunctionTarget::BehindItem(adt.source(ctx.sema.db)?.syntax().value.clone()) - } + Some(impl_) => GeneratedFunctionTarget::InImpl(impl_.clone()), + None => GeneratedFunctionTarget::AfterItem(adt.source(ctx.sema.db)?.syntax().value.clone()), }; - Some((target.clone(), get_insert_offset(&target))) + Some(target) } fn assoc_fn_target_info( @@ -505,36 +460,120 @@ fn assoc_fn_target_info( return None; } let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; - let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?; + let target = get_method_target(ctx, &impl_, &adt)?; let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; - Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset)) -} - -fn get_insert_offset(target: &GeneratedFunctionTarget) -> TextSize { - match target { - GeneratedFunctionTarget::BehindItem(it) => it.text_range().end(), - GeneratedFunctionTarget::InEmptyItemList(it) => it.text_range().start() + TextSize::of('{'), - } + Some(TargetInfo::new(target_module, adt_name, target, file)) } #[derive(Clone)] enum GeneratedFunctionTarget { - BehindItem(SyntaxNode), + AfterItem(SyntaxNode), InEmptyItemList(SyntaxNode), + InImpl(ast::Impl), } impl GeneratedFunctionTarget { fn syntax(&self) -> &SyntaxNode { match self { - GeneratedFunctionTarget::BehindItem(it) => it, + GeneratedFunctionTarget::AfterItem(it) => it, GeneratedFunctionTarget::InEmptyItemList(it) => it, + GeneratedFunctionTarget::InImpl(it) => it.syntax(), } } fn parent(&self) -> SyntaxNode { match self { - GeneratedFunctionTarget::BehindItem(it) => it.parent().expect("item without parent"), + GeneratedFunctionTarget::AfterItem(it) => it.parent().expect("item without parent"), GeneratedFunctionTarget::InEmptyItemList(it) => it.clone(), + GeneratedFunctionTarget::InImpl(it) => it.syntax().clone(), + } + } + + fn insert_impl_at(&self, edit: &mut SourceChangeBuilder, impl_: ast::Impl) { + match self { + GeneratedFunctionTarget::AfterItem(item) => { + let item = edit.make_syntax_mut(item.clone()); + let position = if item.parent().is_some() { + ted::Position::after(&item) + } else { + ted::Position::first_child_of(&item) + }; + + let indent = IndentLevel::from_node(&item); + let leading_ws = make::tokens::whitespace(&format!("\n{indent}")); + impl_.indent(indent); + + ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]); + } + GeneratedFunctionTarget::InEmptyItemList(item_list) => { + let item_list = edit.make_syntax_mut(item_list.clone()); + let insert_after = + item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']); + let position = match insert_after { + Some(child) => ted::Position::after(child), + None => ted::Position::first_child_of(&item_list), + }; + + let indent = IndentLevel::from_node(&item_list); + let leading_indent = indent + 1; + let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}")); + impl_.indent(indent); + + ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]); + } + GeneratedFunctionTarget::InImpl(_) => { + unreachable!("can't insert an impl inside an impl") + } + } + } + + fn insert_fn_at(&self, edit: &mut SourceChangeBuilder, func: ast::Fn) { + match self { + GeneratedFunctionTarget::AfterItem(item) => { + let item = edit.make_syntax_mut(item.clone()); + let position = if item.parent().is_some() { + ted::Position::after(&item) + } else { + ted::Position::first_child_of(&item) + }; + + let indent = IndentLevel::from_node(&item); + let leading_ws = make::tokens::whitespace(&format!("\n\n{indent}")); + func.indent(indent); + + ted::insert_all_raw( + position, + vec![leading_ws.into(), func.syntax().clone().into()], + ); + } + GeneratedFunctionTarget::InEmptyItemList(item_list) => { + let item_list = edit.make_syntax_mut(item_list.clone()); + let insert_after = + item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']); + let position = match insert_after { + Some(child) => ted::Position::after(child), + None => ted::Position::first_child_of(&item_list), + }; + + let indent = IndentLevel::from_node(&item_list); + let leading_indent = indent + 1; + let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}")); + let trailing_ws = make::tokens::whitespace(&format!("\n{indent}")); + func.indent(leading_indent); + + ted::insert_all( + position, + vec![leading_ws.into(), func.syntax().clone().into(), trailing_ws.into()], + ); + } + GeneratedFunctionTarget::InImpl(impl_) => { + let impl_ = edit.make_mut(impl_.clone()); + + let leading_indent = impl_.indent_level() + 1; + func.indent(leading_indent); + + impl_.get_or_create_assoc_item_list().add_item(func.into()); + } } } } @@ -1026,43 +1065,40 @@ fn next_space_for_fn_after_call_site(expr: ast::CallableExpr) -> Option, -) -> Option<(FileId, GeneratedFunctionTarget)> { - let file = module_source.file_id.original_file(db); + db: &dyn hir::db::HirDatabase, + target_module: hir::Module, +) -> (FileId, GeneratedFunctionTarget) { + let module_source = target_module.definition_source(db); + let file = module_source.file_id.original_file(db.upcast()); let assist_item = match &module_source.value { hir::ModuleSource::SourceFile(it) => match it.items().last() { - Some(last_item) => GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()), - None => GeneratedFunctionTarget::BehindItem(it.syntax().clone()), + Some(last_item) => GeneratedFunctionTarget::AfterItem(last_item.syntax().clone()), + None => GeneratedFunctionTarget::AfterItem(it.syntax().clone()), }, hir::ModuleSource::Module(it) => match it.item_list().and_then(|it| it.items().last()) { - Some(last_item) => GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()), - None => GeneratedFunctionTarget::InEmptyItemList(it.item_list()?.syntax().clone()), + Some(last_item) => GeneratedFunctionTarget::AfterItem(last_item.syntax().clone()), + None => { + let item_list = + it.item_list().expect("module definition source should have an item list"); + GeneratedFunctionTarget::InEmptyItemList(item_list.syntax().clone()) + } }, hir::ModuleSource::BlockExpr(it) => { if let Some(last_item) = it.statements().take_while(|stmt| matches!(stmt, ast::Stmt::Item(_))).last() { - GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) + GeneratedFunctionTarget::AfterItem(last_item.syntax().clone()) } else { GeneratedFunctionTarget::InEmptyItemList(it.syntax().clone()) } } }; - Some((file, assist_item)) -} -fn next_space_for_fn_in_impl(impl_: &ast::Impl) -> Option { - let assoc_item_list = impl_.assoc_item_list()?; - if let Some(last_item) = assoc_item_list.assoc_items().last() { - Some(GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())) - } else { - Some(GeneratedFunctionTarget::InEmptyItemList(assoc_item_list.syntax().clone())) - } + (file, assist_item) } #[derive(Clone, Copy)] diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 5b9cc5f66cde..2eb7089b7c38 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -315,17 +315,6 @@ fn inline( } else { fn_body.clone_for_update() }; - if let Some(imp) = body.syntax().ancestors().find_map(ast::Impl::cast) { - if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) { - if let Some(t) = imp.self_ty() { - body.syntax() - .descendants_with_tokens() - .filter_map(NodeOrToken::into_token) - .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) - .for_each(|tok| ted::replace(tok, t.syntax())); - } - } - } let usages_for_locals = |local| { Definition::Local(local) .usages(sema) @@ -381,6 +370,27 @@ fn inline( } } + // We should place the following code after last usage of `usages_for_locals` + // because `ted::replace` will change the offset in syntax tree, which makes + // `FileReference` incorrect + if let Some(imp) = + sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast) + { + if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) { + if let Some(t) = imp.self_ty() { + while let Some(self_tok) = body + .syntax() + .descendants_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) + { + let replace_with = t.clone_subtree().syntax().clone_for_update(); + ted::replace(self_tok, replace_with); + } + } + } + } + let mut func_let_vars: BTreeSet = BTreeSet::new(); // grab all of the local variable declarations in the function @@ -1510,4 +1520,106 @@ fn main() { "#, ); } + + #[test] + fn inline_call_with_multiple_self_types_eq() { + check_assist( + inline_call, + r#" +#[derive(PartialEq, Eq)] +enum Enum { + A, + B, +} + +impl Enum { + fn a_or_b_eq(&self) -> bool { + self == &Self::A || self == &Self::B + } +} + +fn a() -> bool { + Enum::A.$0a_or_b_eq() +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Enum { + A, + B, +} + +impl Enum { + fn a_or_b_eq(&self) -> bool { + self == &Self::A || self == &Self::B + } +} + +fn a() -> bool { + { + let ref this = Enum::A; + this == &Enum::A || this == &Enum::B + } +} +"#, + ) + } + + #[test] + fn inline_call_with_self_type_in_macros() { + check_assist( + inline_call, + r#" +trait Trait { + fn f(a: T1) -> Self; +} + +macro_rules! impl_from { + ($t: ty) => { + impl Trait<$t> for $t { + fn f(a: $t) -> Self { + a as Self + } + } + }; +} + +struct A {} + +impl_from!(A); + +fn main() { + let a: A = A{}; + let b = >::$0f(a); +} +"#, + r#" +trait Trait { + fn f(a: T1) -> Self; +} + +macro_rules! impl_from { + ($t: ty) => { + impl Trait<$t> for $t { + fn f(a: $t) -> Self { + a as Self + } + } + }; +} + +struct A {} + +impl_from!(A); + +fn main() { + let a: A = A{}; + let b = { + let a = a; + a as A + }; +} +"#, + ) + } } diff --git a/crates/ide-assists/src/handlers/introduce_named_generic.rs b/crates/ide-assists/src/handlers/introduce_named_generic.rs index b0d35c02d67b..b1daa7802ed8 100644 --- a/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/crates/ide-assists/src/handlers/introduce_named_generic.rs @@ -18,7 +18,7 @@ use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; // ``` pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let impl_trait_type = ctx.find_node_at_offset::()?; - let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?; + let param = impl_trait_type.syntax().ancestors().find_map(|node| ast::Param::cast(node))?; let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?; let type_bound_list = impl_trait_type.type_bound_list()?; @@ -31,15 +31,16 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_> |edit| { let impl_trait_type = edit.make_mut(impl_trait_type); let fn_ = edit.make_mut(fn_); - - let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type); + let fn_generic_param_list = fn_.get_or_create_generic_param_list(); + let type_param_name = + suggest_name::for_impl_trait_as_generic(&impl_trait_type, &fn_generic_param_list); let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list)) .clone_for_update(); let new_ty = make::ty(&type_param_name).clone_for_update(); ted::replace(impl_trait_type.syntax(), new_ty.syntax()); - fn_.get_or_create_generic_param_list().add_generic_param(type_param.into()); + fn_generic_param_list.add_generic_param(type_param.into()); if let Some(cap) = ctx.config.snippet_cap { if let Some(generic_param) = @@ -111,12 +112,19 @@ fn foo<$0B: Bar #[test] fn replace_impl_trait_with_exist_generic_letter() { - // FIXME: This is wrong, we should pick a different name if the one we - // want is already bound. check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B) {}"#, + r#"fn foo(bar: B0) {}"#, + ); + } + + #[test] + fn replace_impl_trait_with_more_exist_generic_letter() { + check_assist( + introduce_named_generic, + r#"fn foo(bar: $0impl Bar) {}"#, + r#"fn foo(bar: B2) {}"#, ); } @@ -149,4 +157,22 @@ fn foo< r#"fn foo<$0F: Foo + Bar>(bar: F) {}"#, ); } + + #[test] + fn replace_impl_with_mut() { + check_assist( + introduce_named_generic, + r#"fn f(iter: &mut $0impl Iterator) {}"#, + r#"fn f<$0I: Iterator>(iter: &mut I) {}"#, + ); + } + + #[test] + fn replace_impl_inside() { + check_assist( + introduce_named_generic, + r#"fn f(x: &mut Vec<$0impl Iterator>) {}"#, + r#"fn f<$0I: Iterator>(x: &mut Vec) {}"#, + ); + } } diff --git a/crates/ide-assists/src/handlers/promote_local_to_const.rs b/crates/ide-assists/src/handlers/promote_local_to_const.rs index 6ed9bd85fcc9..67fea772c795 100644 --- a/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -11,7 +11,10 @@ use syntax::{ ted, AstNode, WalkEvent, }; -use crate::assist_context::{AssistContext, Assists}; +use crate::{ + assist_context::{AssistContext, Assists}, + utils, +}; // Assist: promote_local_to_const // @@ -79,15 +82,13 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) let name_ref = make::name_ref(&name); for usage in usages { - let Some(usage) = usage.name.as_name_ref().cloned() else { continue }; - if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage) { - let record_field = edit.make_mut(record_field); - let name_expr = - make::expr_path(make::path_from_text(&name)).clone_for_update(); - record_field.replace_expr(name_expr); + let Some(usage_name) = usage.name.as_name_ref().cloned() else { continue }; + if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage_name) { + let name_expr = make::expr_path(make::path_from_text(&name)); + utils::replace_record_field_expr(ctx, edit, record_field, name_expr); } else { - let usage = edit.make_mut(usage); - ted::replace(usage.syntax(), name_ref.clone_for_update().syntax()); + let usage_range = usage.range; + edit.replace(usage_range, name_ref.syntax().text()); } } } @@ -212,6 +213,76 @@ fn main() { ) } + #[test] + fn usage_in_macro() { + check_assist( + promote_local_to_const, + r" +macro_rules! identity { + ($body:expr) => { + $body + } +} + +fn baz() -> usize { + let $0foo = 2; + identity![foo] +} +", + r" +macro_rules! identity { + ($body:expr) => { + $body + } +} + +fn baz() -> usize { + const $0FOO: usize = 2; + identity![FOO] +} +", + ) + } + + #[test] + fn usage_shorthand_in_macro() { + check_assist( + promote_local_to_const, + r" +struct Foo { + foo: usize, +} + +macro_rules! identity { + ($body:expr) => { + $body + }; +} + +fn baz() -> Foo { + let $0foo = 2; + identity![Foo { foo }] +} +", + r" +struct Foo { + foo: usize, +} + +macro_rules! identity { + ($body:expr) => { + $body + }; +} + +fn baz() -> Foo { + const $0FOO: usize = 2; + identity![Foo { foo: FOO }] +} +", + ) + } + #[test] fn not_applicable_non_const_meth_call() { cov_mark::check!(promote_local_non_const); diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs index ee44064e7c5e..859ed1476c45 100644 --- a/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -423,7 +423,7 @@ mod z { struct X(); struct Y(); mod z { - use super::{X}; + use super::X; fn w() { let x = X(); @@ -495,7 +495,7 @@ struct X(); mod y { struct Y(); mod z { - use crate::{X}; + use crate::X; fn f() { let x = X(); } @@ -526,7 +526,7 @@ struct X(); mod y { struct Y(); mod z { - use crate::{y::Y}; + use crate::y::Y; fn f() { let y = Y(); } @@ -536,6 +536,184 @@ mod y { ); } + #[test] + fn remove_unused_auto_remove_brace_nested() { + check_assist( + remove_unused_imports, + r#" +mod a { + pub struct A(); +} +mod b { + struct F(); + mod c { + $0use {{super::{{ + {d::{{{{{{{S, U}}}}}}}}, + {{{{e::{H, L, {{{R}}}}}}}}, + F, super::a::A + }}}};$0 + fn f() { + let f = F(); + let l = L(); + let a = A(); + let s = S(); + let h = H(); + } + } + + mod d { + pub struct S(); + pub struct U(); + } + + mod e { + pub struct H(); + pub struct L(); + pub struct R(); + } +} +"#, + r#" +mod a { + pub struct A(); +} +mod b { + struct F(); + mod c { + use super::{ + d::S, + e::{H, L}, + F, super::a::A + }; + fn f() { + let f = F(); + let l = L(); + let a = A(); + let s = S(); + let h = H(); + } + } + + mod d { + pub struct S(); + pub struct U(); + } + + mod e { + pub struct H(); + pub struct L(); + pub struct R(); + } +} +"#, + ); + } + + #[test] + fn remove_comma_after_auto_remove_brace() { + check_assist( + remove_unused_imports, + r#" +mod m { + pub mod x { + pub struct A; + pub struct B; + } + pub mod y { + pub struct C; + } +} + +$0use m::{ + x::{A, B}, + y::C, +};$0 + +fn main() { + B; +} +"#, + r#" +mod m { + pub mod x { + pub struct A; + pub struct B; + } + pub mod y { + pub struct C; + } +} + +use m:: + x::B +; + +fn main() { + B; +} +"#, + ); + check_assist( + remove_unused_imports, + r#" +mod m { + pub mod x { + pub struct A; + pub struct B; + } + pub mod y { + pub struct C; + pub struct D; + } + pub mod z { + pub struct E; + pub struct F; + } +} + +$0use m::{ + x::{A, B}, + y::{C, D,}, + z::{E, F}, +};$0 + +fn main() { + B; + C; + F; +} +"#, + r#" +mod m { + pub mod x { + pub struct A; + pub struct B; + } + pub mod y { + pub struct C; + pub struct D; + } + pub mod z { + pub struct E; + pub struct F; + } +} + +use m::{ + x::B, + y::C, + z::F, +}; + +fn main() { + B; + C; + F; +} +"#, + ); + } + #[test] fn remove_nested_all_unused() { check_assist( diff --git a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index b1daaea1ed1b..09759019baa7 100644 --- a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -1,4 +1,7 @@ -use syntax::ast::{self, AstNode}; +use syntax::{ + ast::{self, make, AstNode}, + ted, +}; use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; @@ -42,19 +45,34 @@ pub(crate) fn replace_is_method_with_if_let_method( suggest_name::for_variable(&receiver, &ctx.sema) }; - let target = call_expr.syntax().text_range(); - let (assist_id, message, text) = if name_ref.text() == "is_some" { ("replace_is_some_with_if_let_some", "Replace `is_some` with `if let Some`", "Some") } else { ("replace_is_ok_with_if_let_ok", "Replace `is_ok` with `if let Ok`", "Ok") }; - acc.add(AssistId(assist_id, AssistKind::RefactorRewrite), message, target, |edit| { - let var_name = format!("${{0:{}}}", var_name); - let replacement = format!("let {}({}) = {}", text, var_name, receiver); - edit.replace(target, replacement); - }) + acc.add( + AssistId(assist_id, AssistKind::RefactorRewrite), + message, + call_expr.syntax().text_range(), + |edit| { + let call_expr = edit.make_mut(call_expr); + + let var_pat = make::ident_pat(false, false, make::name(&var_name)); + let pat = make::tuple_struct_pat(make::ext::ident_path(text), [var_pat.into()]); + let let_expr = make::expr_let(pat.into(), receiver).clone_for_update(); + + if let Some(cap) = ctx.config.snippet_cap { + if let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat() { + if let Some(first_var) = pat.fields().next() { + edit.add_placeholder_snippet(cap, first_var); + } + } + } + + ted::replace(call_expr.syntax(), let_expr.syntax()); + }, + ) } _ => return None, } diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index 25b3d6d9da91..95b9eb52948f 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -5,13 +5,14 @@ mod sourcegen; use expect_test::expect; use hir::Semantics; use ide_db::{ - base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}, + base_db::{FileId, FileRange, SourceDatabaseExt}, imports::insert_use::{ImportGranularity, InsertUseConfig}, source_change::FileSystemEdit, RootDatabase, SnippetCap, }; use stdx::{format_to, trim_indent}; use syntax::TextRange; +use test_fixture::WithFixture; use test_utils::{assert_eq_text, extract_offset}; use crate::{ @@ -504,16 +505,33 @@ pub fn test_some_range(a: int) -> bool { TextEdit { indels: [ Indel { - insert: "let $0var_name = 5;\n ", - delete: 45..45, + insert: "let", + delete: 45..47, }, Indel { insert: "var_name", - delete: 59..60, + delete: 48..60, + }, + Indel { + insert: "=", + delete: 61..81, + }, + Indel { + insert: "5;\n if let 2..6 = var_name {\n true\n } else {\n false\n }", + delete: 82..108, }, ], }, - None, + Some( + SnippetEdit( + [ + ( + 0, + 49..49, + ), + ], + ), + ), ), }, file_system_edits: [], @@ -566,16 +584,33 @@ pub fn test_some_range(a: int) -> bool { TextEdit { indels: [ Indel { - insert: "let $0var_name = 5;\n ", - delete: 45..45, + insert: "let", + delete: 45..47, }, Indel { insert: "var_name", - delete: 59..60, + delete: 48..60, + }, + Indel { + insert: "=", + delete: 61..81, + }, + Indel { + insert: "5;\n if let 2..6 = var_name {\n true\n } else {\n false\n }", + delete: 82..108, }, ], }, - None, + Some( + SnippetEdit( + [ + ( + 0, + 49..49, + ), + ], + ), + ), ), }, file_system_edits: [], diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index da5822bba9c8..0c2331796f9e 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1153,7 +1153,7 @@ impl SomeTrait for B { } fn method_(&mut self) -> bool { - ::method_( &mut self.a ) + ::method_(&mut self.a) } } "#####, diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index f51e99a914e2..927a8e3c19a1 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -813,3 +813,21 @@ fn test_required_hashes() { assert_eq!(3, required_hashes("#ab\"##c")); assert_eq!(5, required_hashes("#ab\"##\"####c")); } + +/// Replaces the record expression, handling field shorthands including inside macros. +pub(crate) fn replace_record_field_expr( + ctx: &AssistContext<'_>, + edit: &mut SourceChangeBuilder, + record_field: ast::RecordExprField, + initializer: ast::Expr, +) { + if let Some(ast::Expr::PathExpr(path_expr)) = record_field.expr() { + // replace field shorthand + let file_range = ctx.sema.original_range(path_expr.syntax()); + edit.insert(file_range.range.end(), format!(": {}", initializer.syntax().text())) + } else if let Some(expr) = record_field.expr() { + // just replace expr + let file_range = ctx.sema.original_range(expr.syntax()); + edit.replace(file_range.range, initializer.syntax().text()); + } +} diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs index 16704d598ef4..b4c6cbff2a4f 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -1,5 +1,7 @@ //! This module contains functions to suggest names for expressions, functions and other items +use std::collections::HashSet; + use hir::Semantics; use ide_db::RootDatabase; use itertools::Itertools; @@ -58,12 +60,59 @@ const USELESS_METHODS: &[&str] = &[ "into_future", ]; -pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { +/// Suggest a unique name for generic parameter. +/// +/// `existing_params` is used to check if the name conflicts with existing +/// generic parameters. +/// +/// The function checks if the name conflicts with existing generic parameters. +/// If so, it will try to resolve the conflict by adding a number suffix, e.g. +/// `T`, `T0`, `T1`, ... +pub(crate) fn for_unique_generic_name( + name: &str, + existing_params: &ast::GenericParamList, +) -> SmolStr { + let param_names = existing_params + .generic_params() + .map(|param| match param { + ast::GenericParam::TypeParam(t) => t.name().unwrap().to_string(), + p => p.to_string(), + }) + .collect::>(); + let mut name = name.to_string(); + let base_len = name.len(); + let mut count = 0; + while param_names.contains(&name) { + name.truncate(base_len); + name.push_str(&count.to_string()); + count += 1; + } + + name.into() +} + +/// Suggest name of impl trait type +/// +/// `existing_params` is used to check if the name conflicts with existing +/// generic parameters. +/// +/// # Current implementation +/// +/// In current implementation, the function tries to get the name from the first +/// character of the name for the first type bound. +/// +/// If the name conflicts with existing generic parameters, it will try to +/// resolve the conflict with `for_unique_generic_name`. +pub(crate) fn for_impl_trait_as_generic( + ty: &ast::ImplTraitType, + existing_params: &ast::GenericParamList, +) -> SmolStr { let c = ty .type_bound_list() .and_then(|bounds| bounds.syntax().text().char_at(0.into())) .unwrap_or('T'); - c.encode_utf8(&mut [0; 4]).into() + + for_unique_generic_name(c.encode_utf8(&mut [0; 4]), existing_params) } /// Suggest name of variable for given expression @@ -275,7 +324,8 @@ fn from_field_name(expr: &ast::Expr) -> Option { #[cfg(test)] mod tests { - use ide_db::base_db::{fixture::WithFixture, FileRange}; + use ide_db::base_db::FileRange; + use test_fixture::WithFixture; use super::*; diff --git a/crates/ide-completion/Cargo.toml b/crates/ide-completion/Cargo.toml index 60f90a41b962..7fbcf3d19e0f 100644 --- a/crates/ide-completion/Cargo.toml +++ b/crates/ide-completion/Cargo.toml @@ -35,3 +35,7 @@ expect-test = "1.4.0" # local deps test-utils.workspace = true +test-fixture.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 466f0b1fb7f9..9155caa2e0b8 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -26,6 +26,7 @@ mod cfg; mod derive; mod lint; mod repr; +mod macro_use; pub(crate) use self::derive::complete_derive_path; @@ -35,6 +36,7 @@ pub(crate) fn complete_known_attribute_input( ctx: &CompletionContext<'_>, &colon_prefix: &bool, fake_attribute_under_caret: &ast::Attr, + extern_crate: Option<&ast::ExternCrate>, ) -> Option<()> { let attribute = fake_attribute_under_caret; let name_ref = match attribute.path() { @@ -66,6 +68,9 @@ pub(crate) fn complete_known_attribute_input( lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints); } "cfg" => cfg::complete_cfg(acc, ctx), + "macro_use" => { + macro_use::complete_macro_use(acc, ctx, extern_crate, &parse_tt_as_comma_sep_paths(tt)?) + } _ => (), } Some(()) diff --git a/crates/ide-completion/src/completions/attribute/macro_use.rs b/crates/ide-completion/src/completions/attribute/macro_use.rs new file mode 100644 index 000000000000..f45f9cba258d --- /dev/null +++ b/crates/ide-completion/src/completions/attribute/macro_use.rs @@ -0,0 +1,35 @@ +//! Completion for macros in `#[macro_use(...)]` +use hir::ModuleDef; +use ide_db::SymbolKind; +use syntax::ast; + +use crate::{context::CompletionContext, item::CompletionItem, Completions}; + +pub(super) fn complete_macro_use( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + extern_crate: Option<&ast::ExternCrate>, + existing_imports: &[ast::Path], +) { + let Some(extern_crate) = extern_crate else { return }; + let Some(extern_crate) = ctx.sema.to_def(extern_crate) else { return }; + let Some(krate) = extern_crate.resolved_crate(ctx.db) else { return }; + + for mod_def in krate.root_module().declarations(ctx.db) { + if let ModuleDef::Macro(mac) = mod_def { + let mac_name = mac.name(ctx.db); + let Some(mac_name) = mac_name.as_str() else { continue }; + + let existing_import = existing_imports + .iter() + .filter_map(|p| p.as_single_name_ref()) + .find(|n| n.text() == mac_name); + if existing_import.is_some() { + continue; + } + + let item = CompletionItem::new(SymbolKind::Macro, ctx.source_range(), mac_name); + item.add_to(acc, ctx.db); + } + } +} diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 613a35dcb108..53a1c8405c2c 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -27,6 +27,8 @@ pub(crate) fn complete_dot( } let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. }); + let is_method_acces_with_parens = + matches!(dot_access.kind, DotAccessKind::Method { has_parens: true }); complete_fields( acc, @@ -35,6 +37,7 @@ pub(crate) fn complete_dot( |acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty), |acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty), is_field_access, + is_method_acces_with_parens, ); complete_methods(ctx, receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None)); @@ -83,6 +86,7 @@ pub(crate) fn complete_undotted_self( }, |acc, field, ty| acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), field, &ty), true, + false, ); complete_methods(ctx, &ty, |func| { acc.add_method( @@ -106,12 +110,14 @@ fn complete_fields( mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type), mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type), is_field_access: bool, + is_method_acess_with_parens: bool, ) { let mut seen_names = FxHashSet::default(); for receiver in receiver.autoderef(ctx.db) { for (field, ty) in receiver.fields(ctx.db) { if seen_names.insert(field.name(ctx.db)) - && (is_field_access || ty.is_fn() || ty.is_closure()) + && (is_field_access + || (is_method_acess_with_parens && (ty.is_fn() || ty.is_closure()))) { named_field(acc, field, ty); } @@ -120,7 +126,8 @@ fn complete_fields( // Tuples are always the last type in a deref chain, so just check if the name is // already seen without inserting into the hashset. if !seen_names.contains(&hir::Name::new_tuple_field(i)) - && (is_field_access || ty.is_fn() || ty.is_closure()) + && (is_field_access + || (is_method_acess_with_parens && (ty.is_fn() || ty.is_closure()))) { // Tuple fields are always public (tuple struct fields are handled above). tuple_index(acc, i, ty); @@ -1236,4 +1243,24 @@ fn foo() { "#, ) } + + #[test] + fn test_fn_field_dot_access_method_has_parens_false() { + check( + r#" +struct Foo { baz: fn() } +impl Foo { + fn bar(self, t: T): T { t } +} + +fn baz() { + let foo = Foo{ baz: || {} }; + foo.ba$0::<>; +} +"#, + expect![[r#" + me bar(…) fn(self, T) + "#]], + ); + } } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 0da7ba6d0001..108b040de6ba 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -371,6 +371,7 @@ pub(super) enum CompletionAnalysis { UnexpandedAttrTT { colon_prefix: bool, fake_attribute_under_caret: Option, + extern_crate: Option, }, } @@ -693,7 +694,7 @@ impl<'a> CompletionContext<'a> { let krate = scope.krate(); let module = scope.module(); - let toolchain = db.crate_graph()[krate.into()].channel; + let toolchain = db.crate_graph()[krate.into()].channel(); // `toolchain == None` means we're in some detached files. Since we have no information on // the toolchain being used, let's just allow unstable items to be listed. let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None); diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 1e6b2f319aad..7da664836574 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -1,7 +1,7 @@ //! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; -use hir::{HasSource, Semantics, Type, TypeInfo, Variant}; +use hir::{Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use syntax::{ algo::{find_node_at_offset, non_trivia_sibling}, @@ -254,11 +254,13 @@ fn analyze( { let colon_prefix = previous_non_trivia_token(self_token.clone()) .map_or(false, |it| T![:] == it.kind()); + CompletionAnalysis::UnexpandedAttrTT { fake_attribute_under_caret: fake_ident_token .parent_ancestors() .find_map(ast::Attr::cast), colon_prefix, + extern_crate: p.ancestors().find_map(ast::ExternCrate::cast), } } else { return None; @@ -359,7 +361,12 @@ fn expected_type_and_name( let ty = it.pat() .and_then(|pat| sema.type_of_pat(&pat)) .or_else(|| it.initializer().and_then(|it| sema.type_of_expr(&it))) - .map(TypeInfo::original); + .map(TypeInfo::original) + .filter(|ty| { + // don't infer the let type if the expr is a function, + // preventing parenthesis from vanishing + it.ty().is_some() || !ty.is_fn() + }); let name = match it.pat() { Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name), Some(_) | None => None, @@ -413,20 +420,16 @@ fn expected_type_and_name( })().unwrap_or((None, None)) }, ast::RecordExprField(it) => { + let field_ty = sema.resolve_record_field(&it).map(|(_, _, ty)| ty); + let field_name = it.field_name().map(NameOrNameRef::NameRef); if let Some(expr) = it.expr() { cov_mark::hit!(expected_type_struct_field_with_leading_char); - ( - sema.type_of_expr(&expr).map(TypeInfo::original), - it.field_name().map(NameOrNameRef::NameRef), - ) + let ty = field_ty + .or_else(|| sema.type_of_expr(&expr).map(TypeInfo::original)); + (ty, field_name) } else { cov_mark::hit!(expected_type_struct_field_followed_by_comma); - let ty = sema.resolve_record_field(&it) - .map(|(_, _, ty)| ty); - ( - ty, - it.field_name().map(NameOrNameRef::NameRef), - ) + (field_ty, field_name) } }, // match foo { $0 } @@ -740,13 +743,13 @@ fn classify_name_ref( match sema.resolve_path(&segment.parent_path().top_path())? { hir::PathResolution::Def(def) => match def { hir::ModuleDef::Function(func) => { - func.source(sema.db)?.value.generic_param_list() + sema.source(func)?.value.generic_param_list() } hir::ModuleDef::Adt(adt) => { - adt.source(sema.db)?.value.generic_param_list() + sema.source(adt)?.value.generic_param_list() } hir::ModuleDef::Variant(variant) => { - variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list() + sema.source(variant.parent_enum(sema.db))?.value.generic_param_list() } hir::ModuleDef::Trait(trait_) => { if let ast::GenericArg::AssocTypeArg(arg) = &arg { @@ -772,14 +775,14 @@ fn classify_name_ref( return None; } else { in_trait = Some(trait_); - trait_.source(sema.db)?.value.generic_param_list() + sema.source(trait_)?.value.generic_param_list() } } hir::ModuleDef::TraitAlias(trait_) => { - trait_.source(sema.db)?.value.generic_param_list() + sema.source(trait_)?.value.generic_param_list() } hir::ModuleDef::TypeAlias(ty_) => { - ty_.source(sema.db)?.value.generic_param_list() + sema.source(ty_)?.value.generic_param_list() } _ => None, }, @@ -788,7 +791,7 @@ fn classify_name_ref( }, ast::MethodCallExpr(call) => { let func = sema.resolve_method_call(&call)?; - func.source(sema.db)?.value.generic_param_list() + sema.source(func)?.value.generic_param_list() }, ast::AssocTypeArg(arg) => { let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?; @@ -805,7 +808,7 @@ fn classify_name_ref( }, _ => None, })?; - assoc_ty.source(sema.db)?.value.generic_param_list() + sema.source(*assoc_ty)?.value.generic_param_list() } _ => None, }, diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 37a2828e8dc8..ff324e7a56d6 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -211,12 +211,14 @@ pub fn completions( CompletionAnalysis::UnexpandedAttrTT { colon_prefix, fake_attribute_under_caret: Some(attr), + extern_crate, } => { completions::attribute::complete_known_attribute_input( acc, ctx, colon_prefix, attr, + extern_crate.as_ref(), ); } CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (), diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 2ea3f74d18bc..581d557e831a 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -837,11 +837,11 @@ fn main() { } "#, expect![[r#" - fn main [] - fn test [] + fn main() [] + fn test(…) [] md dep [] fn function (use dep::test_mod_a::function) [requires_import] - fn function (use dep::test_mod_b::function) [requires_import] + fn function(…) (use dep::test_mod_b::function) [requires_import] "#]], ); } diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index d23ed71fdcc6..b306bede653b 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -305,12 +305,15 @@ fn params( return None; } - // Don't add parentheses if the expected type is some function reference. - if let Some(ty) = &ctx.expected_type { - // FIXME: check signature matches? - if ty.is_fn() { - cov_mark::hit!(no_call_parens_if_fn_ptr_needed); - return None; + // Don't add parentheses if the expected type is a function reference with the same signature. + if let Some(expected) = ctx.expected_type.as_ref().filter(|e| e.is_fn()) { + if let Some(expected) = expected.as_callable(ctx.db) { + if let Some(completed) = func.ty(ctx.db).as_callable(ctx.db) { + if expected.sig() == completed.sig() { + cov_mark::hit!(no_call_parens_if_fn_ptr_needed); + return None; + } + } } } diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index f28afacc586f..f13754e2ded0 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -26,12 +26,13 @@ mod visibility; use expect_test::Expect; use hir::PrefixKind; use ide_db::{ - base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, + base_db::{FileLoader, FilePosition}, imports::insert_use::{ImportGranularity, InsertUseConfig}, RootDatabase, SnippetCap, }; use itertools::Itertools; use stdx::{format_to, trim_indent}; +use test_fixture::ChangeFixture; use test_utils::assert_eq_text; use crate::{ diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs index d8c134c533b3..351abe9850b9 100644 --- a/crates/ide-completion/src/tests/attribute.rs +++ b/crates/ide-completion/src/tests/attribute.rs @@ -1067,3 +1067,82 @@ mod repr { ); } } + +mod macro_use { + use super::*; + + #[test] + fn completes_macros() { + check( + r#" +//- /dep.rs crate:dep +#[macro_export] +macro_rules! foo { + () => {}; +} + +#[macro_export] +macro_rules! bar { + () => {}; +} + +//- /main.rs crate:main deps:dep +#[macro_use($0)] +extern crate dep; +"#, + expect![[r#" + ma bar + ma foo + "#]], + ) + } + + #[test] + fn only_completes_exported_macros() { + check( + r#" +//- /dep.rs crate:dep +#[macro_export] +macro_rules! foo { + () => {}; +} + +macro_rules! bar { + () => {}; +} + +//- /main.rs crate:main deps:dep +#[macro_use($0)] +extern crate dep; +"#, + expect![[r#" + ma foo + "#]], + ) + } + + #[test] + fn does_not_completes_already_imported_macros() { + check( + r#" +//- /dep.rs crate:dep +#[macro_export] +macro_rules! foo { + () => {}; +} + +#[macro_export] +macro_rules! bar { + () => {}; +} + +//- /main.rs crate:main deps:dep +#[macro_use(foo, $0)] +extern crate dep; +"#, + expect![[r#" + ma bar + "#]], + ) + } +} diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml index 4a2e770f193a..f14d9ed1b933 100644 --- a/crates/ide-db/Cargo.toml +++ b/crates/ide-db/Cargo.toml @@ -16,11 +16,11 @@ cov-mark = "2.0.0-pre.1" tracing.workspace = true rayon.workspace = true fst = { version = "0.4.7", default-features = false } -rustc-hash = "1.1.0" +rustc-hash.workspace = true once_cell = "1.17.0" either.workspace = true itertools.workspace = true -arrayvec = "0.7.2" +arrayvec.workspace = true indexmap.workspace = true memchr = "2.6.4" triomphe.workspace = true @@ -34,6 +34,7 @@ profile.workspace = true stdx.workspace = true syntax.workspace = true text-edit.workspace = true +span.workspace = true # ide should depend only on the top-level `hir` package. if you need # something from some `hir-xxx` subpackage, reexport the API via `hir`. hir.workspace = true @@ -47,4 +48,8 @@ xshell.workspace = true # local deps test-utils.workspace = true +test-fixture.workspace = true sourcegen.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs index 343be870c9ee..db6cd128e83d 100644 --- a/crates/ide-db/src/apply_change.rs +++ b/crates/ide-db/src/apply_change.rs @@ -5,13 +5,13 @@ use base_db::{ debug::{DebugQueryTable, TableEntry}, Database, Durability, Query, QueryTable, }, - Change, SourceRootId, + SourceRootId, }; use profile::{memory_usage, Bytes}; use rustc_hash::FxHashSet; use triomphe::Arc; -use crate::{symbol_index::SymbolsDatabase, RootDatabase}; +use crate::{symbol_index::SymbolsDatabase, Change, RootDatabase}; impl RootDatabase { pub fn request_cancellation(&mut self) { @@ -23,7 +23,7 @@ impl RootDatabase { let _p = profile::span("RootDatabase::apply_change"); self.request_cancellation(); tracing::trace!("apply_change {:?}", change); - if let Some(roots) = &change.roots { + if let Some(roots) = &change.source_change.roots { let mut local_roots = FxHashSet::default(); let mut library_roots = FxHashSet::default(); for (idx, root) in roots.iter().enumerate() { @@ -87,7 +87,6 @@ impl RootDatabase { // SourceDatabase base_db::ParseQuery base_db::CrateGraphQuery - base_db::ProcMacrosQuery // SourceDatabaseExt base_db::FileTextQuery @@ -104,6 +103,7 @@ impl RootDatabase { hir::db::MacroArgQuery hir::db::ParseMacroExpansionQuery hir::db::RealSpanMapQuery + hir::db::ProcMacrosQuery // DefDatabase hir::db::FileItemTreeQuery diff --git a/crates/ide-db/src/documentation.rs b/crates/ide-db/src/documentation.rs index 26f3cd28a276..cc8e8431708a 100644 --- a/crates/ide-db/src/documentation.rs +++ b/crates/ide-db/src/documentation.rs @@ -138,15 +138,13 @@ pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option { for doc in docs { // str::lines doesn't yield anything for the empty string if !doc.is_empty() { - buf.extend(Itertools::intersperse( - doc.lines().map(|line| { - line.char_indices() - .nth(indent) - .map_or(line, |(offset, _)| &line[offset..]) - .trim_end() - }), - "\n", - )); + // We don't trim trailing whitespace from doc comments as multiple trailing spaces + // indicates a hard line break in Markdown. + let lines = doc.lines().map(|line| { + line.char_indices().nth(indent).map_or(line, |(offset, _)| &line[offset..]) + }); + + buf.extend(Itertools::intersperse(lines, "\n")); } buf.push('\n'); } diff --git a/crates/ide-db/src/imports/insert_use/tests.rs b/crates/ide-db/src/imports/insert_use/tests.rs index 01d2f1970c38..a3abce896424 100644 --- a/crates/ide-db/src/imports/insert_use/tests.rs +++ b/crates/ide-db/src/imports/insert_use/tests.rs @@ -1,6 +1,6 @@ -use base_db::fixture::WithFixture; use hir::PrefixKind; use stdx::trim_indent; +use test_fixture::WithFixture; use test_utils::{assert_eq_text, CURSOR_MARKER}; use super::*; diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index fefc05e53550..128971994f64 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -43,6 +43,8 @@ pub mod syntax_helpers { pub use parser::LexedStr; } +pub use hir::Change; + use std::{fmt, mem::ManuallyDrop}; use base_db::{ diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index fb4c0c12691d..8c1a6e6e40b8 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -82,6 +82,34 @@ impl<'a> PathTransform<'a> { } } + pub fn impl_transformation( + target_scope: &'a SemanticsScope<'a>, + source_scope: &'a SemanticsScope<'a>, + impl_: hir::Impl, + generic_arg_list: ast::GenericArgList, + ) -> PathTransform<'a> { + PathTransform { + source_scope, + target_scope, + generic_def: Some(impl_.into()), + substs: get_type_args_from_arg_list(generic_arg_list).unwrap_or_default(), + } + } + + pub fn adt_transformation( + target_scope: &'a SemanticsScope<'a>, + source_scope: &'a SemanticsScope<'a>, + adt: hir::Adt, + generic_arg_list: ast::GenericArgList, + ) -> PathTransform<'a> { + PathTransform { + source_scope, + target_scope, + generic_def: Some(adt.into()), + substs: get_type_args_from_arg_list(generic_arg_list).unwrap_or_default(), + } + } + pub fn generic_transformation( target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>, diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index d2b6a732689c..7f28965885ad 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -22,9 +22,10 @@ //! Our current behavior is ¯\_(ツ)_/¯. use std::fmt; -use base_db::{span::SyntaxContextId, AnchoredPathBuf, FileId, FileRange}; +use base_db::{AnchoredPathBuf, FileId, FileRange}; use either::Either; use hir::{FieldSource, HasSource, HirFileIdExt, InFile, ModuleSource, Semantics}; +use span::SyntaxContextId; use stdx::{never, TupleExt}; use syntax::{ ast::{self, HasName}, @@ -515,7 +516,7 @@ fn source_edit_from_def( if let Definition::Local(local) = def { let mut file_id = None; for source in local.sources(sema.db) { - let source = match source.source.clone().original_ast_node(sema.db) { + let source = match source.source.clone().original_ast_node_rooted(sema.db) { Some(source) => source, None => match source .source @@ -559,7 +560,7 @@ fn source_edit_from_def( } } else { // Foo { ref mut field } -> Foo { field: ref mut new_name } - // ^ insert `field: ` + // original_ast_node_rootedd: ` // ^^^^^ replace this with `new_name` edit.insert( pat.syntax().text_range().start(), diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index be8566b759cf..f5f0f0576f22 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -378,9 +378,9 @@ impl Query { #[cfg(test)] mod tests { - use base_db::fixture::WithFixture; use expect_test::expect_file; use hir::symbols::SymbolCollector; + use test_fixture::WithFixture; use super::*; @@ -414,6 +414,12 @@ impl Struct { fn impl_fn() {} } +struct StructT; + +impl StructT { + fn generic_impl_fn() {} +} + trait Trait { fn trait_fn(&self); } diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index c9875c7f8f29..f0b97779c736 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -23,12 +23,12 @@ ), ptr: SyntaxNodePtr { kind: TYPE_ALIAS, - range: 397..417, + range: 470..490, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 402..407, + range: 475..480, }, ), }, @@ -51,12 +51,12 @@ ), ptr: SyntaxNodePtr { kind: CONST, - range: 340..361, + range: 413..434, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 346..351, + range: 419..424, }, ), }, @@ -79,12 +79,12 @@ ), ptr: SyntaxNodePtr { kind: CONST, - range: 520..592, + range: 593..665, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 526..542, + range: 599..615, }, ), }, @@ -139,12 +139,12 @@ ), ptr: SyntaxNodePtr { kind: USE_TREE, - range: 654..676, + range: 727..749, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 663..676, + range: 736..749, }, ), }, @@ -197,12 +197,12 @@ ), ptr: SyntaxNodePtr { kind: STATIC, - range: 362..396, + range: 435..469, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 369..375, + range: 442..448, }, ), }, @@ -276,7 +276,7 @@ Struct( Struct { id: StructId( - 4, + 5, ), }, ), @@ -287,12 +287,12 @@ ), ptr: SyntaxNodePtr { kind: STRUCT, - range: 318..336, + range: 391..409, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 325..335, + range: 398..408, }, ), }, @@ -308,7 +308,7 @@ Struct( Struct { id: StructId( - 5, + 6, ), }, ), @@ -319,12 +319,12 @@ ), ptr: SyntaxNodePtr { kind: STRUCT, - range: 555..581, + range: 628..654, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 562..580, + range: 635..653, }, ), }, @@ -340,7 +340,7 @@ Struct( Struct { id: StructId( - 6, + 7, ), }, ), @@ -351,12 +351,42 @@ ), ptr: SyntaxNodePtr { kind: STRUCT, - range: 479..507, + range: 552..580, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 486..506, + range: 559..579, + }, + ), + }, + container_name: None, + is_alias: false, + is_assoc: false, + }, + FileSymbol { + name: "StructT", + def: Adt( + Struct( + Struct { + id: StructId( + 2, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 261..279, + }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 268..275, }, ), }, @@ -379,12 +409,12 @@ ), ptr: SyntaxNodePtr { kind: TRAIT, - range: 261..300, + range: 334..373, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 267..272, + range: 340..345, }, ), }, @@ -409,12 +439,12 @@ ), ptr: SyntaxNodePtr { kind: USE_TREE, - range: 682..696, + range: 755..769, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 691..696, + range: 764..769, }, ), }, @@ -469,12 +499,12 @@ ), ptr: SyntaxNodePtr { kind: MODULE, - range: 419..457, + range: 492..530, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 423..428, + range: 496..501, }, ), }, @@ -499,12 +529,12 @@ ), ptr: SyntaxNodePtr { kind: MODULE, - range: 594..604, + range: 667..677, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 598..603, + range: 671..676, }, ), }, @@ -542,6 +572,36 @@ is_alias: false, is_assoc: false, }, + FileSymbol { + name: "generic_impl_fn", + def: Function( + Function { + id: FunctionId( + 3, + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: FN, + range: 307..330, + }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 310..325, + }, + ), + }, + container_name: Some( + "StructT", + ), + is_alias: false, + is_assoc: true, + }, FileSymbol { name: "impl_fn", def: Function( @@ -566,7 +626,9 @@ }, ), }, - container_name: None, + container_name: Some( + "Struct", + ), is_alias: false, is_assoc: true, }, @@ -615,12 +677,12 @@ ), ptr: SyntaxNodePtr { kind: FN, - range: 302..338, + range: 375..411, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 305..309, + range: 378..382, }, ), }, @@ -645,12 +707,12 @@ ), ptr: SyntaxNodePtr { kind: USE_TREE, - range: 611..648, + range: 684..721, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 628..648, + range: 701..721, }, ), }, @@ -673,12 +735,12 @@ ), ptr: SyntaxNodePtr { kind: FN, - range: 279..298, + range: 352..371, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 282..290, + range: 355..363, }, ), }, @@ -705,7 +767,7 @@ Struct( Struct { id: StructId( - 2, + 3, ), }, ), @@ -716,12 +778,12 @@ ), ptr: SyntaxNodePtr { kind: STRUCT, - range: 435..455, + range: 508..528, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 442..454, + range: 515..527, }, ), }, @@ -776,7 +838,7 @@ Struct( Struct { id: StructId( - 3, + 4, ), }, ), @@ -836,7 +898,7 @@ Struct( Struct { id: StructId( - 3, + 4, ), }, ), @@ -866,7 +928,7 @@ Struct( Struct { id: StructId( - 3, + 4, ), }, ), diff --git a/crates/ide-db/src/traits.rs b/crates/ide-db/src/traits.rs index 9abbc3441429..bbdfd81d653f 100644 --- a/crates/ide-db/src/traits.rs +++ b/crates/ide-db/src/traits.rs @@ -113,10 +113,11 @@ fn assoc_item_of_trait( #[cfg(test)] mod tests { - use base_db::{fixture::ChangeFixture, FilePosition}; + use base_db::FilePosition; use expect_test::{expect, Expect}; use hir::Semantics; use syntax::ast::{self, AstNode}; + use test_fixture::ChangeFixture; use crate::RootDatabase; diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml index f4055024cc32..3ed48457a284 100644 --- a/crates/ide-diagnostics/Cargo.toml +++ b/crates/ide-diagnostics/Cargo.toml @@ -32,7 +32,11 @@ expect-test = "1.4.0" # local deps test-utils.workspace = true +test-fixture.workspace = true sourcegen.workspace = true [features] in-rust-tree = [] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index 820014391467..c202264bb566 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -76,4 +76,24 @@ impl Marker for Foo { "#, ) } + + #[test] + fn dont_work_for_negative_impl() { + check_diagnostics( + r#" +trait Marker { + const FLAG: bool = false; + fn boo(); + fn foo () {} +} +struct Foo; +impl !Marker for Foo { + type T = i32; + const FLAG: bool = true; + fn bar() {} + fn boo() {} +} + "#, + ) + } } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs b/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs new file mode 100644 index 000000000000..f1c95993c843 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs @@ -0,0 +1,52 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: unresolved-assoc-item +// +// This diagnostic is triggered if the referenced associated item does not exist. +pub(crate) fn unresolved_assoc_item( + ctx: &DiagnosticsContext<'_>, + d: &hir::UnresolvedAssocItem, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0599"), + "no such associated item", + d.expr_or_pat.clone().map(Into::into), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn bare() { + check_diagnostics( + r#" +struct S; + +fn main() { + let _ = S::Assoc; + //^^^^^^^^ error: no such associated item +} +"#, + ); + } + + #[test] + fn unimplemented_trait() { + check_diagnostics( + r#" +struct S; +trait Foo { + const X: u32; +} + +fn main() { + let _ = S::X; + //^^^^ error: no such associated item +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 464b0a710ea7..60a45a05a4a1 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -1,11 +1,14 @@ -use hir::{db::ExpandDatabase, HirDisplay}; +use hir::{db::ExpandDatabase, AssocItem, HirDisplay, InFile}; use ide_db::{ assists::{Assist, AssistId, AssistKind}, base_db::FileRange, label::Label, source_change::SourceChange, }; -use syntax::{ast, AstNode, TextRange}; +use syntax::{ + ast::{self, make, HasArgList}, + AstNode, SmolStr, TextRange, +}; use text_edit::TextEdit; use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext}; @@ -17,15 +20,17 @@ pub(crate) fn unresolved_method( ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall, ) -> Diagnostic { - let field_suffix = if d.field_with_same_name.is_some() { + let suffix = if d.field_with_same_name.is_some() { ", but a field with a similar name exists" + } else if d.assoc_func_with_same_name.is_some() { + ", but an associated function with a similar name exists" } else { "" }; Diagnostic::new( DiagnosticCode::RustcHardError("E0599"), format!( - "no method `{}` on type `{}`{field_suffix}", + "no method `{}` on type `{}`{suffix}", d.name.display(ctx.sema.db), d.receiver.display(ctx.sema.db) ), @@ -46,11 +51,27 @@ pub(crate) fn unresolved_method( } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option> { - if let Some(ty) = &d.field_with_same_name { + let field_fix = if let Some(ty) = &d.field_with_same_name { field_fix(ctx, d, ty) } else { // FIXME: add quickfix None + }; + + let assoc_func_fix = assoc_func_fix(ctx, d); + + let mut fixes = vec![]; + if let Some(field_fix) = field_fix { + fixes.push(field_fix); + } + if let Some(assoc_func_fix) = assoc_func_fix { + fixes.push(assoc_func_fix); + } + + if fixes.is_empty() { + None + } else { + Some(fixes) } } @@ -58,7 +79,7 @@ fn field_fix( ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall, ty: &hir::Type, -) -> Option> { +) -> Option { if !ty.impls_fnonce(ctx.sema.db) { return None; } @@ -78,7 +99,7 @@ fn field_fix( } _ => return None, }; - Some(vec![Assist { + Some(Assist { id: AssistId("expected-method-found-field-fix", AssistKind::QuickFix), label: Label::new("Use parentheses to call the value of the field".to_string()), group: None, @@ -88,13 +109,180 @@ fn field_fix( (file_id, TextEdit::insert(range.end(), ")".to_owned())), ])), trigger_signature_help: false, - }]) + }) +} + +fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option { + if let Some(assoc_item_id) = d.assoc_func_with_same_name { + let db = ctx.sema.db; + + let expr_ptr = &d.expr; + let root = db.parse_or_expand(expr_ptr.file_id); + let expr: ast::Expr = expr_ptr.value.to_node(&root); + + let call = ast::MethodCallExpr::cast(expr.syntax().clone())?; + let range = InFile::new(expr_ptr.file_id, call.syntax().text_range()) + .original_node_file_range_rooted(db) + .range; + + let receiver = call.receiver()?; + let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original; + + let need_to_take_receiver_as_first_arg = match hir::AssocItem::from(assoc_item_id) { + AssocItem::Function(f) => { + let assoc_fn_params = f.assoc_fn_params(db); + if assoc_fn_params.is_empty() { + false + } else { + assoc_fn_params + .first() + .map(|first_arg| { + // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example, + // type of `b` is `Self`, which is `Box`, containing unspecified generics. + // However, type of `receiver` is specified, it could be `Box` or something like that, + // so `first_arg.ty() == receiver_type` evaluate to `false` here. + // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard, + // apply `.as_adt()` over `Box` or `Box` gets `Box`, so we get `true` here. + + // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver` + first_arg.ty() == receiver_type + || first_arg.ty().as_adt() == receiver_type.as_adt() + }) + .unwrap_or(false) + } + } + _ => false, + }; + + let mut receiver_type_adt_name = receiver_type.as_adt()?.name(db).to_smol_str().to_string(); + + let generic_parameters: Vec = receiver_type.generic_parameters(db).collect(); + // if receiver should be pass as first arg in the assoc func, + // we could omit generic parameters cause compiler can deduce it automatically + if !need_to_take_receiver_as_first_arg && !generic_parameters.is_empty() { + let generic_parameters = generic_parameters.join(", ").to_string(); + receiver_type_adt_name = + format!("{}::<{}>", receiver_type_adt_name, generic_parameters); + } + + let method_name = call.name_ref()?; + let assoc_func_call = format!("{}::{}()", receiver_type_adt_name, method_name); + + let assoc_func_call = make::expr_path(make::path_from_text(&assoc_func_call)); + + let args: Vec<_> = if need_to_take_receiver_as_first_arg { + std::iter::once(receiver).chain(call.arg_list()?.args()).collect() + } else { + call.arg_list()?.args().collect() + }; + let args = make::arg_list(args); + + let assoc_func_call_expr_string = make::expr_call(assoc_func_call, args).to_string(); + + let file_id = ctx.sema.original_range_opt(call.receiver()?.syntax())?.file_id; + + Some(Assist { + id: AssistId("method_call_to_assoc_func_call_fix", AssistKind::QuickFix), + label: Label::new(format!( + "Use associated func call instead: `{}`", + assoc_func_call_expr_string + )), + group: None, + target: range, + source_change: Some(SourceChange::from_text_edit( + file_id, + TextEdit::replace(range, assoc_func_call_expr_string), + )), + trigger_signature_help: false, + }) + } else { + None + } } #[cfg(test)] mod tests { use crate::tests::{check_diagnostics, check_fix}; + #[test] + fn test_assoc_func_fix() { + check_fix( + r#" +struct A {} + +impl A { + fn hello() {} +} +fn main() { + let a = A{}; + a.hello$0(); +} +"#, + r#" +struct A {} + +impl A { + fn hello() {} +} +fn main() { + let a = A{}; + A::hello(); +} +"#, + ); + } + + #[test] + fn test_assoc_func_diagnostic() { + check_diagnostics( + r#" +struct A {} +impl A { + fn hello() {} +} +fn main() { + let a = A{}; + a.hello(); + // ^^^^^ 💡 error: no method `hello` on type `A`, but an associated function with a similar name exists +} +"#, + ); + } + + #[test] + fn test_assoc_func_fix_with_generic() { + check_fix( + r#" +struct A { + a: T, + b: U +} + +impl A { + fn foo() {} +} +fn main() { + let a = A {a: 0, b: ""}; + a.foo()$0; +} +"#, + r#" +struct A { + a: T, + b: U +} + +impl A { + fn foo() {} +} +fn main() { + let a = A {a: 0, b: ""}; + A::::foo(); +} +"#, + ); + } + #[test] fn smoke_test() { check_diagnostics( diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 579386c72ef4..c7ad09e7ebdd 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -51,6 +51,7 @@ mod handlers { pub(crate) mod typed_hole; pub(crate) mod type_mismatch; pub(crate) mod unimplemented_builtin_macro; + pub(crate) mod unresolved_assoc_item; pub(crate) mod unresolved_extern_crate; pub(crate) mod unresolved_field; pub(crate) mod unresolved_method; @@ -371,7 +372,8 @@ pub fn diagnostics( AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d), AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d), AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), - AnyDiagnostic::UnreachableLabel(d) => handlers::unreachable_label:: unreachable_label(&ctx, &d), + AnyDiagnostic::UnreachableLabel(d) => handlers::unreachable_label::unreachable_label(&ctx, &d), + AnyDiagnostic::UnresolvedAssocItem(d) => handlers::unresolved_assoc_item::unresolved_assoc_item(&ctx, &d), AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d), AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d), AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d), diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index 48e0363c9ca8..67912a3a03eb 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -3,12 +3,11 @@ mod sourcegen; use expect_test::Expect; use ide_db::{ - assists::AssistResolveStrategy, - base_db::{fixture::WithFixture, SourceDatabaseExt}, - LineIndexDatabase, RootDatabase, + assists::AssistResolveStrategy, base_db::SourceDatabaseExt, LineIndexDatabase, RootDatabase, }; use itertools::Itertools; use stdx::trim_indent; +use test_fixture::WithFixture; use test_utils::{assert_eq_text, extract_annotations, MiniCore}; use crate::{DiagnosticsConfig, ExprFillDefaultMode, Severity}; diff --git a/crates/ide-ssr/Cargo.toml b/crates/ide-ssr/Cargo.toml index 56b29f92b829..57b1f9465ad3 100644 --- a/crates/ide-ssr/Cargo.toml +++ b/crates/ide-ssr/Cargo.toml @@ -31,3 +31,7 @@ expect-test = "1.4.0" # local deps test-utils.workspace = true +test-fixture.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/ide-ssr/src/tests.rs b/crates/ide-ssr/src/tests.rs index 424ba3d7fd50..7c7d146cb4a8 100644 --- a/crates/ide-ssr/src/tests.rs +++ b/crates/ide-ssr/src/tests.rs @@ -65,8 +65,8 @@ fn parser_undefined_placeholder_in_replacement() { /// `code` may optionally contain a cursor marker `$0`. If it doesn't, then the position will be /// the start of the file. If there's a second cursor marker, then we'll return a single range. pub(crate) fn single_file(code: &str) -> (ide_db::RootDatabase, FilePosition, Vec) { - use ide_db::base_db::fixture::WithFixture; use ide_db::symbol_index::SymbolsDatabase; + use test_fixture::{WithFixture, WORKSPACE}; let (mut db, file_id, range_or_offset) = if code.contains(test_utils::CURSOR_MARKER) { ide_db::RootDatabase::with_range_or_offset(code) } else { @@ -86,7 +86,7 @@ pub(crate) fn single_file(code: &str) -> (ide_db::RootDatabase, FilePosition, Ve } } let mut local_roots = FxHashSet::default(); - local_roots.insert(ide_db::base_db::fixture::WORKSPACE); + local_roots.insert(WORKSPACE); db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); (db, position, selections) } diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index 0943574ec1b5..9f0a2f30f658 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" crossbeam-channel = "0.5.5" -arrayvec = "0.7.4" +arrayvec.workspace = true either.workspace = true itertools.workspace = true tracing.workspace = true @@ -50,6 +50,10 @@ expect-test = "1.4.0" # local deps test-utils.workspace = true +test-fixture.workspace = true [features] in-rust-tree = ["ide-assists/in-rust-tree", "ide-diagnostics/in-rust-tree"] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index d7f82b4af3e1..f49c5af0af1f 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -3,8 +3,9 @@ use ide_db::{ base_db::{FileId, FilePosition, FileRange}, defs::Definition, helpers::visit_file_defs, - RootDatabase, + FxHashSet, RootDatabase, }; +use itertools::Itertools; use syntax::{ast::HasName, AstNode, TextRange}; use crate::{ @@ -23,13 +24,13 @@ mod fn_references; // and running/debugging binaries. // // image::https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png[] -#[derive(Debug)] +#[derive(Debug, Hash, PartialEq, Eq)] pub struct Annotation { pub range: TextRange, pub kind: AnnotationKind, } -#[derive(Debug)] +#[derive(Debug, Hash, PartialEq, Eq)] pub enum AnnotationKind { Runnable(Runnable), HasImpls { pos: FilePosition, data: Option> }, @@ -56,7 +57,7 @@ pub(crate) fn annotations( config: &AnnotationConfig, file_id: FileId, ) -> Vec { - let mut annotations = Vec::default(); + let mut annotations = FxHashSet::default(); if config.annotate_runnables { for runnable in runnables(db, file_id) { @@ -66,7 +67,7 @@ pub(crate) fn annotations( let range = runnable.nav.focus_or_full_range(); - annotations.push(Annotation { range, kind: AnnotationKind::Runnable(runnable) }); + annotations.insert(Annotation { range, kind: AnnotationKind::Runnable(runnable) }); } } @@ -99,13 +100,13 @@ pub(crate) fn annotations( }) .for_each(|range| { let (annotation_range, target_position) = mk_ranges(range); - annotations.push(Annotation { + annotations.insert(Annotation { range: annotation_range, kind: AnnotationKind::HasReferences { pos: target_position, data: None, }, - }) + }); }) } if config.annotate_references || config.annotate_impls { @@ -131,14 +132,14 @@ pub(crate) fn annotations( }; let (annotation_range, target_pos) = mk_ranges(range); if config.annotate_impls && !matches!(def, Definition::Const(_)) { - annotations.push(Annotation { + annotations.insert(Annotation { range: annotation_range, kind: AnnotationKind::HasImpls { pos: target_pos, data: None }, }); } if config.annotate_references { - annotations.push(Annotation { + annotations.insert(Annotation { range: annotation_range, kind: AnnotationKind::HasReferences { pos: target_pos, data: None }, }); @@ -149,7 +150,7 @@ pub(crate) fn annotations( node: InFile, source_file_id: FileId, ) -> Option<(TextRange, Option)> { - if let Some(InRealFile { file_id, value }) = node.original_ast_node(db) { + if let Some(InRealFile { file_id, value }) = node.original_ast_node_rooted(db) { if file_id == source_file_id { return Some(( value.syntax().text_range(), @@ -171,7 +172,7 @@ pub(crate) fn annotations( })); } - annotations + annotations.into_iter().sorted_by_key(|a| (a.range.start(), a.range.end())).collect() } pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation { @@ -252,25 +253,6 @@ fn main() { "#, expect![[r#" [ - Annotation { - range: 53..57, - kind: Runnable( - Runnable { - use_name_in_title: false, - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 50..85, - focus_range: 53..57, - name: "main", - kind: Function, - }, - kind: Bin, - cfg: None, - }, - ), - }, Annotation { range: 6..10, kind: HasReferences { @@ -306,6 +288,25 @@ fn main() { ), }, }, + Annotation { + range: 53..57, + kind: Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 50..85, + focus_range: 53..57, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + ), + }, Annotation { range: 53..57, kind: HasReferences { @@ -337,39 +338,6 @@ fn main() { "#, expect![[r#" [ - Annotation { - range: 17..21, - kind: Runnable( - Runnable { - use_name_in_title: false, - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 14..48, - focus_range: 17..21, - name: "main", - kind: Function, - }, - kind: Bin, - cfg: None, - }, - ), - }, - Annotation { - range: 7..11, - kind: HasImpls { - pos: FilePosition { - file_id: FileId( - 0, - ), - offset: 7, - }, - data: Some( - [], - ), - }, - }, Annotation { range: 7..11, kind: HasReferences { @@ -391,6 +359,39 @@ fn main() { ), }, }, + Annotation { + range: 7..11, + kind: HasImpls { + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, + data: Some( + [], + ), + }, + }, + Annotation { + range: 17..21, + kind: Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 14..48, + focus_range: 17..21, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + ), + }, Annotation { range: 17..21, kind: HasReferences { @@ -426,49 +427,6 @@ fn main() { "#, expect![[r#" [ - Annotation { - range: 69..73, - kind: Runnable( - Runnable { - use_name_in_title: false, - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 66..100, - focus_range: 69..73, - name: "main", - kind: Function, - }, - kind: Bin, - cfg: None, - }, - ), - }, - Annotation { - range: 7..11, - kind: HasImpls { - pos: FilePosition { - file_id: FileId( - 0, - ), - offset: 7, - }, - data: Some( - [ - NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 36..64, - focus_range: 57..61, - name: "impl", - kind: Impl, - }, - ], - ), - }, - }, Annotation { range: 7..11, kind: HasReferences { @@ -496,6 +454,30 @@ fn main() { ), }, }, + Annotation { + range: 7..11, + kind: HasImpls { + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, + data: Some( + [ + NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 36..64, + focus_range: 57..61, + name: "impl", + kind: Impl, + }, + ], + ), + }, + }, Annotation { range: 20..31, kind: HasImpls { @@ -555,6 +537,25 @@ fn main() { ), }, }, + Annotation { + range: 69..73, + kind: Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 66..100, + focus_range: 69..73, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + ), + }, ] "#]], ); @@ -622,49 +623,6 @@ fn main() { "#, expect![[r#" [ - Annotation { - range: 61..65, - kind: Runnable( - Runnable { - use_name_in_title: false, - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 58..95, - focus_range: 61..65, - name: "main", - kind: Function, - }, - kind: Bin, - cfg: None, - }, - ), - }, - Annotation { - range: 7..11, - kind: HasImpls { - pos: FilePosition { - file_id: FileId( - 0, - ), - offset: 7, - }, - data: Some( - [ - NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 14..56, - focus_range: 19..23, - name: "impl", - kind: Impl, - }, - ], - ), - }, - }, Annotation { range: 7..11, kind: HasReferences { @@ -692,6 +650,30 @@ fn main() { ), }, }, + Annotation { + range: 7..11, + kind: HasImpls { + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, + data: Some( + [ + NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 14..56, + focus_range: 19..23, + name: "impl", + kind: Impl, + }, + ], + ), + }, + }, Annotation { range: 33..44, kind: HasReferences { @@ -727,6 +709,25 @@ fn main() { ), }, }, + Annotation { + range: 61..65, + kind: Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 58..95, + focus_range: 61..65, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + ), + }, ] "#]], ); @@ -745,6 +746,20 @@ mod tests { "#, expect![[r#" [ + Annotation { + range: 3..7, + kind: HasReferences { + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 3, + }, + data: Some( + [], + ), + }, + }, Annotation { range: 3..7, kind: Runnable( @@ -812,20 +827,6 @@ mod tests { }, ), }, - Annotation { - range: 3..7, - kind: HasReferences { - pos: FilePosition { - file_id: FileId( - 0, - ), - offset: 3, - }, - data: Some( - [], - ), - }, - }, ] "#]], ); @@ -877,7 +878,7 @@ struct Foo; [ Annotation { range: 0..71, - kind: HasImpls { + kind: HasReferences { pos: FilePosition { file_id: FileId( 0, @@ -891,7 +892,7 @@ struct Foo; }, Annotation { range: 0..71, - kind: HasReferences { + kind: HasImpls { pos: FilePosition { file_id: FileId( 0, diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 9760f9daf0a3..a36082bafcf2 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -492,7 +492,7 @@ fn get_doc_base_urls( let Some(krate) = def.krate(db) else { return Default::default() }; let Some(display_name) = krate.display_name(db) else { return Default::default() }; let crate_data = &db.crate_graph()[krate.into()]; - let channel = crate_data.channel.map_or("nightly", ReleaseChannel::as_str); + let channel = crate_data.channel().unwrap_or(ReleaseChannel::Nightly).as_str(); let (web_base, local_base) = match &crate_data.origin { // std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself. diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs index 2e5903c0602e..3b19b85c4bc1 100644 --- a/crates/ide/src/fixture.rs +++ b/crates/ide/src/fixture.rs @@ -1,5 +1,5 @@ //! Utilities for creating `Analysis` instances for tests. -use ide_db::base_db::fixture::ChangeFixture; +use test_fixture::ChangeFixture; use test_utils::{extract_annotations, RangeOrOffset}; use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 7491879a67fb..e0beba8fb380 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -4,7 +4,7 @@ use crate::{ doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget, RangeInfo, TryToNav, }; -use hir::{AsAssocItem, AssocItem, DescendPreference, Semantics}; +use hir::{AsAssocItem, AssocItem, DescendPreference, ModuleDef, Semantics}; use ide_db::{ base_db::{AnchoredPath, FileId, FileLoader}, defs::{Definition, IdentClass}, @@ -73,10 +73,15 @@ pub(crate) fn goto_definition( .into_iter() .filter_map(|token| { let parent = token.parent()?; + if let Some(tt) = ast::TokenTree::cast(parent.clone()) { if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) { return Some(vec![x]); } + + if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.clone()) { + return Some(vec![x]); + } } Some( IdentClass::classify_node(sema, &parent)? @@ -140,6 +145,27 @@ fn try_lookup_include_path( }) } +fn try_lookup_macro_def_in_macro_use( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> Option { + let extern_crate = token.parent()?.ancestors().find_map(ast::ExternCrate::cast)?; + let extern_crate = sema.to_def(&extern_crate)?; + let krate = extern_crate.resolved_crate(sema.db)?; + + for mod_def in krate.root_module().declarations(sema.db) { + if let ModuleDef::Macro(mac) = mod_def { + if mac.name(sema.db).as_str() == Some(token.text()) { + if let Some(nav) = mac.try_to_nav(sema.db) { + return Some(nav.call_site); + } + } + } + } + + None +} + /// finds the trait definition of an impl'd item, except function /// e.g. /// ```rust @@ -2081,4 +2107,47 @@ fn test() { "#, ); } + + #[test] + fn goto_macro_def_from_macro_use() { + check( + r#" +//- /main.rs crate:main deps:mac +#[macro_use(foo$0)] +extern crate mac; + +//- /mac.rs crate:mac +#[macro_export] +macro_rules! foo { + //^^^ + () => {}; +} + "#, + ); + + check( + r#" +//- /main.rs crate:main deps:mac +#[macro_use(foo, bar$0, baz)] +extern crate mac; + +//- /mac.rs crate:mac +#[macro_export] +macro_rules! foo { + () => {}; +} + +#[macro_export] +macro_rules! bar { + //^^^ + () => {}; +} + +#[macro_export] +macro_rules! baz { + () => {}; +} + "#, + ); + } } diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 6384db39d7c6..c1a4a7b1fc79 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -4,7 +4,6 @@ use ide_db::{ helpers::pick_best_token, RootDatabase, }; -use itertools::Itertools; use syntax::{ast, AstNode, SyntaxKind::*, T}; use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; @@ -34,10 +33,10 @@ pub(crate) fn goto_implementation( })?; let range = original_token.text_range(); let navs = - sema.descend_into_macros(DescendPreference::None, original_token) - .into_iter() - .filter_map(|token| token.parent().and_then(ast::NameLike::cast)) - .filter_map(|node| match &node { + sema.descend_into_macros_single(DescendPreference::SameText, original_token) + .parent() + .and_then(ast::NameLike::cast) + .and_then(|node| match &node { ast::NameLike::Name(name) => { NameClass::classify(&sema, name).and_then(|class| match class { NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it), @@ -52,8 +51,7 @@ pub(crate) fn goto_implementation( }), ast::NameLike::Lifetime(_) => None, }) - .unique() - .filter_map(|def| { + .and_then(|def| { let navs = match def { Definition::Trait(trait_) => impls_for_trait(&sema, trait_), Definition::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)), @@ -75,8 +73,7 @@ pub(crate) fn goto_implementation( }; Some(navs) }) - .flatten() - .collect(); + .unwrap_or_default(); Some(RangeInfo { range, info: navs }) } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index a19952e4cae9..6ff16b9e2f71 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -67,6 +67,7 @@ use std::ffi::OsStr; use cfg::CfgOptions; use fetch_crates::CrateInfo; +use hir::Change; use ide_db::{ base_db::{ salsa::{self, ParallelDatabase}, @@ -122,7 +123,7 @@ pub use ide_completion::{ }; pub use ide_db::{ base_db::{ - Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, + Cancelled, CrateGraph, CrateId, Edition, FileChange, FileId, FilePosition, FileRange, SourceRoot, SourceRootId, }, documentation::Documentation, @@ -183,7 +184,7 @@ impl AnalysisHost { /// Applies changes to the current state of the world. If there are /// outstanding snapshots, they will be canceled. pub fn apply_change(&mut self, change: Change) { - self.db.apply_change(change) + self.db.apply_change(change); } /// NB: this clears the database diff --git a/crates/ide/src/markdown_remove.rs b/crates/ide/src/markdown_remove.rs index 718868c8747b..fa01875e2048 100644 --- a/crates/ide/src/markdown_remove.rs +++ b/crates/ide/src/markdown_remove.rs @@ -6,6 +6,7 @@ use pulldown_cmark::{Event, Parser, Tag}; /// Currently limited in styling, i.e. no ascii tables or lists pub(crate) fn remove_markdown(markdown: &str) -> String { let mut out = String::new(); + out.reserve_exact(markdown.len()); let parser = Parser::new(markdown); for event in parser { @@ -13,10 +14,7 @@ pub(crate) fn remove_markdown(markdown: &str) -> String { Event::Text(text) | Event::Code(text) => out.push_str(&text), Event::SoftBreak => out.push(' '), Event::HardBreak | Event::Rule | Event::End(Tag::CodeBlock(_)) => out.push('\n'), - Event::End(Tag::Paragraph) => { - out.push('\n'); - out.push('\n'); - } + Event::End(Tag::Paragraph) => out.push_str("\n\n"), Event::Start(_) | Event::End(_) | Event::Html(_) @@ -25,7 +23,10 @@ pub(crate) fn remove_markdown(markdown: &str) -> String { } } - if let Some(p) = out.rfind(|c| c != '\n') { + if let Some(mut p) = out.rfind(|c| c != '\n') { + while !out.is_char_boundary(p + 1) { + p += 1; + } out.drain(p + 1..); } @@ -153,4 +154,10 @@ book] or the [Reference]. For more information on the various types of functions and how they're used, consult the Rust book or the Reference."#]].assert_eq(&res); } + + #[test] + fn on_char_boundary() { + expect!["a┘"].assert_eq(&remove_markdown("```text\na┘\n```")); + expect!["وقار"].assert_eq(&remove_markdown("```\nوقار\n```\n")); + } } diff --git a/crates/ide/src/shuffle_crate_graph.rs b/crates/ide/src/shuffle_crate_graph.rs index f85700daf1f7..bf6ad47a4952 100644 --- a/crates/ide/src/shuffle_crate_graph.rs +++ b/crates/ide/src/shuffle_crate_graph.rs @@ -1,5 +1,6 @@ +use hir::{db::ExpandDatabase, ProcMacros}; use ide_db::{ - base_db::{salsa::Durability, CrateGraph, ProcMacros, SourceDatabase}, + base_db::{salsa::Durability, CrateGraph, SourceDatabase}, FxHashMap, RootDatabase, }; use triomphe::Arc; @@ -39,7 +40,7 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { data.is_proc_macro, data.origin.clone(), data.target_layout.clone(), - data.channel, + data.toolchain.clone(), ); new_proc_macros.insert(new_id, proc_macros[&old_id].clone()); map.insert(old_id, new_id); diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 990376a49659..483fb76d91cf 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -646,8 +646,9 @@ mod tests { use std::iter; use expect_test::{expect, Expect}; - use ide_db::base_db::{fixture::ChangeFixture, FilePosition}; + use ide_db::base_db::FilePosition; use stdx::format_to; + use test_fixture::ChangeFixture; use crate::RootDatabase; diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs index d8d81869a2f8..f0d18fdefa71 100644 --- a/crates/ide/src/ssr.rs +++ b/crates/ide/src/ssr.rs @@ -59,10 +59,11 @@ mod tests { use expect_test::expect; use ide_assists::{Assist, AssistResolveStrategy}; use ide_db::{ - base_db::{fixture::WithFixture, salsa::Durability, FileRange}, + base_db::{salsa::Durability, FileRange}, symbol_index::SymbolsDatabase, FxHashSet, RootDatabase, }; + use test_fixture::WithFixture; use triomphe::Arc; use super::ssr_assists; @@ -70,7 +71,7 @@ mod tests { fn get_assists(ra_fixture: &str, resolve: AssistResolveStrategy) -> Vec { let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture); let mut local_roots = FxHashSet::default(); - local_roots.insert(ide_db::base_db::fixture::WORKSPACE); + local_roots.insert(test_fixture::WORKSPACE); db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); ssr_assists(&db, &resolve, FileRange { file_id, range: range_or_offset.into() }) } diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index e7f97ebe6f7b..b2b305c1d380 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs @@ -10,7 +10,7 @@ use ide_db::{ debug::{DebugQueryTable, TableEntry}, Query, QueryTable, }, - CrateId, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId, + CrateData, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId, }, symbol_index::ModuleSymbolsQuery, }; @@ -54,25 +54,54 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { format_to!(buf, "{} block def maps\n", collect_query_count(BlockDefMapQuery.in_db(db))); if let Some(file_id) = file_id { - format_to!(buf, "\nFile info:\n"); + format_to!(buf, "\nCrates for file {}:\n", file_id.index()); let crates = crate::parent_module::crates_for(db, file_id); if crates.is_empty() { format_to!(buf, "Does not belong to any crate"); } let crate_graph = db.crate_graph(); - for krate in crates { - let display_crate = |krate: CrateId| match &crate_graph[krate].display_name { - Some(it) => format!("{it}({})", krate.into_raw()), - None => format!("{}", krate.into_raw()), - }; - format_to!(buf, "Crate: {}\n", display_crate(krate)); - format_to!(buf, "Enabled cfgs: {:?}\n", crate_graph[krate].cfg_options); - let deps = crate_graph[krate] - .dependencies + for crate_id in crates { + let CrateData { + root_file_id, + edition, + version, + display_name, + cfg_options, + potential_cfg_options, + env, + dependencies, + origin, + is_proc_macro, + target_layout, + toolchain, + } = &crate_graph[crate_id]; + format_to!( + buf, + "Crate: {}\n", + match display_name { + Some(it) => format!("{it}({})", crate_id.into_raw()), + None => format!("{}", crate_id.into_raw()), + } + ); + format_to!(buf, " Root module file id: {}\n", root_file_id.index()); + format_to!(buf, " Edition: {}\n", edition); + format_to!(buf, " Version: {}\n", version.as_deref().unwrap_or("n/a")); + format_to!(buf, " Enabled cfgs: {:?}\n", cfg_options); + format_to!(buf, " Potential cfgs: {:?}\n", potential_cfg_options); + format_to!(buf, " Env: {:?}\n", env); + format_to!(buf, " Origin: {:?}\n", origin); + format_to!(buf, " Is a proc macro crate: {}\n", is_proc_macro); + format_to!(buf, " Workspace Target Layout: {:?}\n", target_layout); + format_to!( + buf, + " Workspace Toolchain: {}\n", + toolchain.as_ref().map_or_else(|| "n/a".into(), |v| v.to_string()) + ); + let deps = dependencies .iter() - .map(|dep| format!("{}={:?}", dep.name, dep.crate_id)) + .map(|dep| format!("{}={}", dep.name, dep.crate_id.into_raw())) .format(", "); - format_to!(buf, "Dependencies: {}\n", deps); + format_to!(buf, " Dependencies: {}\n", deps); } } diff --git a/crates/intern/Cargo.toml b/crates/intern/Cargo.toml index d9184b0fb6fe..67b4164ce1fe 100644 --- a/crates/intern/Cargo.toml +++ b/crates/intern/Cargo.toml @@ -16,5 +16,8 @@ doctest = false # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap.workspace = true hashbrown.workspace = true -rustc-hash = "1.1.0" +rustc-hash.workspace = true triomphe.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/limit/Cargo.toml b/crates/limit/Cargo.toml index c08886909922..c89722cc40de 100644 --- a/crates/limit/Cargo.toml +++ b/crates/limit/Cargo.toml @@ -11,3 +11,6 @@ rust-version.workspace = true [features] tracking = [] default = ["tracking"] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/load-cargo/Cargo.toml b/crates/load-cargo/Cargo.toml index 31b9f6c76d06..dcab6328a4e8 100644 --- a/crates/load-cargo/Cargo.toml +++ b/crates/load-cargo/Cargo.toml @@ -12,7 +12,7 @@ authors.workspace = true [dependencies] anyhow.workspace = true -crossbeam-channel = "0.5.5" +crossbeam-channel.workspace = true itertools.workspace = true tracing.workspace = true @@ -23,3 +23,9 @@ project-model.workspace = true tt.workspace = true vfs.workspace = true vfs-notify.workspace = true +span.workspace = true + +hir-expand.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index db9654220dd7..556ed73a04c2 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -5,17 +5,19 @@ use std::{collections::hash_map::Entry, mem, path::Path, sync}; use crossbeam_channel::{unbounded, Receiver}; -use ide::{AnalysisHost, Change, SourceRoot}; +use hir_expand::proc_macro::{ + ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacroLoadResult, + ProcMacros, +}; +use ide::{AnalysisHost, SourceRoot}; use ide_db::{ - base_db::{ - span::SpanData, CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, - ProcMacroKind, ProcMacroLoadResult, ProcMacros, - }, - FxHashMap, + base_db::{CrateGraph, Env}, + Change, FxHashMap, }; use itertools::Itertools; use proc_macro_api::{MacroDylib, ProcMacroServer}; use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; +use span::Span; use tt::DelimSpan; use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath}; @@ -374,13 +376,13 @@ struct Expander(proc_macro_api::ProcMacro); impl ProcMacroExpander for Expander { fn expand( &self, - subtree: &tt::Subtree, - attrs: Option<&tt::Subtree>, + subtree: &tt::Subtree, + attrs: Option<&tt::Subtree>, env: &Env, - def_site: SpanData, - call_site: SpanData, - mixed_site: SpanData, - ) -> Result, ProcMacroExpansionError> { + def_site: Span, + call_site: Span, + mixed_site: Span, + ) -> Result, ProcMacroExpansionError> { let env = env.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(); match self.0.expand(subtree, attrs, env, def_site, call_site, mixed_site) { Ok(Ok(subtree)) => Ok(subtree), @@ -397,13 +399,13 @@ struct IdentityExpander; impl ProcMacroExpander for IdentityExpander { fn expand( &self, - subtree: &tt::Subtree, - _: Option<&tt::Subtree>, + subtree: &tt::Subtree, + _: Option<&tt::Subtree>, _: &Env, - _: SpanData, - _: SpanData, - _: SpanData, - ) -> Result, ProcMacroExpansionError> { + _: Span, + _: Span, + _: Span, + ) -> Result, ProcMacroExpansionError> { Ok(subtree.clone()) } } @@ -415,13 +417,13 @@ struct EmptyExpander; impl ProcMacroExpander for EmptyExpander { fn expand( &self, - _: &tt::Subtree, - _: Option<&tt::Subtree>, + _: &tt::Subtree, + _: Option<&tt::Subtree>, _: &Env, - call_site: SpanData, - _: SpanData, - _: SpanData, - ) -> Result, ProcMacroExpansionError> { + call_site: Span, + _: Span, + _: Span, + ) -> Result, ProcMacroExpansionError> { Ok(tt::Subtree::empty(DelimSpan { open: call_site, close: call_site })) } } diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml index adab1003d103..f50d796e139f 100644 --- a/crates/mbe/Cargo.toml +++ b/crates/mbe/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -rustc-hash = "1.1.0" +rustc-hash.workspace = true smallvec.workspace = true tracing.workspace = true @@ -22,6 +22,10 @@ syntax.workspace = true parser.workspace = true tt.workspace = true stdx.workspace = true +span.workspace = true [dev-dependencies] test-utils.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index f503aecce2c2..6c3917b37f1f 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs @@ -20,7 +20,10 @@ fn benchmark_parse_macro_rules() { let rules = macro_rules_fixtures_tt(); let hash: usize = { let _pt = bench("mbe parse macro rules"); - rules.values().map(|it| DeclarativeMacro::parse_macro_rules(it, true).rules.len()).sum() + rules + .values() + .map(|it| DeclarativeMacro::parse_macro_rules(it, true, true).rules.len()) + .sum() }; assert_eq!(hash, 1144); } @@ -38,7 +41,7 @@ fn benchmark_expand_macro_rules() { invocations .into_iter() .map(|(id, tt)| { - let res = rules[&id].expand(&tt, |_| ()); + let res = rules[&id].expand(&tt, |_| (), true, DUMMY); assert!(res.err.is_none()); res.value.token_trees.len() }) @@ -50,7 +53,7 @@ fn benchmark_expand_macro_rules() { fn macro_rules_fixtures() -> FxHashMap> { macro_rules_fixtures_tt() .into_iter() - .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true))) + .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true, true))) .collect() } @@ -64,8 +67,11 @@ fn macro_rules_fixtures_tt() -> FxHashMap .filter_map(ast::MacroRules::cast) .map(|rule| { let id = rule.name().unwrap().to_string(); - let def_tt = - syntax_node_to_token_tree(rule.token_tree().unwrap().syntax(), DummyTestSpanMap); + let def_tt = syntax_node_to_token_tree( + rule.token_tree().unwrap().syntax(), + DummyTestSpanMap, + DUMMY, + ); (id, def_tt) }) .collect() @@ -105,7 +111,7 @@ fn invocation_fixtures( for op in rule.lhs.iter() { collect_from_op(op, &mut subtree, &mut seed); } - if it.expand(&subtree, |_| ()).err.is_none() { + if it.expand(&subtree, |_| (), true, DUMMY).err.is_none() { res.push((name.clone(), subtree)); break; } @@ -199,7 +205,7 @@ fn invocation_fixtures( }); parent.token_trees.push(subtree.into()); } - Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {} + Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. } => {} }; // Simple linear congruential generator for deterministic result diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs index 0e755f69bf7d..60483809dc18 100644 --- a/crates/mbe/src/expander.rs +++ b/crates/mbe/src/expander.rs @@ -16,6 +16,8 @@ pub(crate) fn expand_rules( input: &tt::Subtree, marker: impl Fn(&mut S) + Copy, is_2021: bool, + new_meta_vars: bool, + call_site: S, ) -> ExpandResult> { let mut match_: Option<(matcher::Match, &crate::Rule)> = None; for rule in rules { @@ -25,8 +27,13 @@ pub(crate) fn expand_rules( // If we find a rule that applies without errors, we're done. // Unconditionally returning the transcription here makes the // `test_repeat_bad_var` test fail. - let ExpandResult { value, err: transcribe_err } = - transcriber::transcribe(&rule.rhs, &new_match.bindings, marker); + let ExpandResult { value, err: transcribe_err } = transcriber::transcribe( + &rule.rhs, + &new_match.bindings, + marker, + new_meta_vars, + call_site, + ); if transcribe_err.is_none() { return ExpandResult::ok(value); } @@ -45,11 +52,14 @@ pub(crate) fn expand_rules( if let Some((match_, rule)) = match_ { // if we got here, there was no match without errors let ExpandResult { value, err: transcribe_err } = - transcriber::transcribe(&rule.rhs, &match_.bindings, marker); + transcriber::transcribe(&rule.rhs, &match_.bindings, marker, new_meta_vars, call_site); ExpandResult { value, err: match_.err.or(transcribe_err) } } else { ExpandResult::new( - tt::Subtree { delimiter: tt::Delimiter::DUMMY_INVISIBLE, token_trees: vec![] }, + tt::Subtree { + delimiter: tt::Delimiter::invisible_spanned(call_site), + token_trees: vec![], + }, ExpandError::NoMatchingRule, ) } @@ -121,6 +131,7 @@ enum Binding { #[derive(Debug, Clone, PartialEq, Eq)] enum Fragment { + Empty, /// token fragments are just copy-pasted into the output Tokens(tt::TokenTree), /// Expr ast fragments are surrounded with `()` on insertion to preserve diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 012b02a3f87a..40b4c7cdd656 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -63,7 +63,7 @@ use std::rc::Rc; use smallvec::{smallvec, SmallVec}; use syntax::SmolStr; -use tt::Span; +use tt::{DelimSpan, Span}; use crate::{ expander::{Binding, Bindings, ExpandResult, Fragment}, @@ -74,11 +74,7 @@ use crate::{ impl Bindings { fn push_optional(&mut self, name: &SmolStr) { - // FIXME: Do we have a better way to represent an empty token ? - // Insert an empty subtree for empty token - let tt = - tt::Subtree { delimiter: tt::Delimiter::DUMMY_INVISIBLE, token_trees: vec![] }.into(); - self.inner.insert(name.clone(), Binding::Fragment(Fragment::Tokens(tt))); + self.inner.insert(name.clone(), Binding::Fragment(Fragment::Empty)); } fn push_empty(&mut self, name: &SmolStr) { @@ -387,6 +383,7 @@ fn match_loop_inner<'t, S: Span>( eof_items: &mut SmallVec<[MatchState<'t, S>; 1]>, error_items: &mut SmallVec<[MatchState<'t, S>; 1]>, is_2021: bool, + delim_span: tt::DelimSpan, ) { macro_rules! try_push { ($items: expr, $it:expr) => { @@ -474,7 +471,7 @@ fn match_loop_inner<'t, S: Span>( cur_items.push(new_item); } cur_items.push(MatchState { - dot: tokens.iter_delimited(None), + dot: tokens.iter_delimited(delim_span), stack: Default::default(), up: Some(Box::new(item)), sep: separator.clone(), @@ -489,7 +486,7 @@ fn match_loop_inner<'t, S: Span>( if let Ok(subtree) = src.clone().expect_subtree() { if subtree.delimiter.kind == delimiter.kind { item.stack.push(item.dot); - item.dot = tokens.iter_delimited(Some(*delimiter)); + item.dot = tokens.iter_delimited_with(*delimiter); cur_items.push(item); } } @@ -497,7 +494,7 @@ fn match_loop_inner<'t, S: Span>( OpDelimited::Op(Op::Var { kind, name, .. }) => { if let &Some(kind) = kind { let mut fork = src.clone(); - let match_res = match_meta_var(kind, &mut fork, is_2021); + let match_res = match_meta_var(kind, &mut fork, is_2021, delim_span); match match_res.err { None => { // Some meta variables are optional (e.g. vis) @@ -588,7 +585,9 @@ fn match_loop_inner<'t, S: Span>( item.is_error = true; error_items.push(item); } - OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. }) => { + OpDelimited::Op( + Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. }, + ) => { stdx::never!("metavariable expression in lhs found"); } OpDelimited::Open => { @@ -609,6 +608,7 @@ fn match_loop_inner<'t, S: Span>( } fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match { + let span = src.delimiter.delim_span(); let mut src = TtIter::new(src); let mut stack: SmallVec<[TtIter<'_, S>; 1]> = SmallVec::new(); let mut res = Match::default(); @@ -617,7 +617,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: let mut bindings_builder = BindingsBuilder::default(); let mut cur_items = smallvec![MatchState { - dot: pattern.iter_delimited(None), + dot: pattern.iter_delimited(span), stack: Default::default(), up: None, sep: None, @@ -648,6 +648,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: &mut eof_items, &mut error_items, is_2021, + span, ); stdx::always!(cur_items.is_empty()); @@ -761,12 +762,13 @@ fn match_meta_var( kind: MetaVarKind, input: &mut TtIter<'_, S>, is_2021: bool, + delim_span: DelimSpan, ) -> ExpandResult>> { let fragment = match kind { MetaVarKind::Path => { - return input - .expect_fragment(parser::PrefixEntryPoint::Path) - .map(|it| it.map(tt::TokenTree::subtree_or_wrap).map(Fragment::Path)); + return input.expect_fragment(parser::PrefixEntryPoint::Path).map(|it| { + it.map(|it| tt::TokenTree::subtree_or_wrap(it, delim_span)).map(Fragment::Path) + }); } MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop, @@ -795,7 +797,7 @@ fn match_meta_var( return input.expect_fragment(parser::PrefixEntryPoint::Expr).map(|tt| { tt.map(|tt| match tt { tt::TokenTree::Leaf(leaf) => tt::Subtree { - delimiter: tt::Delimiter::dummy_invisible(), + delimiter: tt::Delimiter::invisible_spanned(*leaf.span()), token_trees: vec![leaf.into()], }, tt::TokenTree::Subtree(mut s) => { @@ -829,7 +831,7 @@ fn match_meta_var( match neg { None => lit.into(), Some(neg) => tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::dummy_invisible(), + delimiter: tt::Delimiter::invisible_spanned(*literal.span()), token_trees: vec![neg, lit.into()], }), } @@ -851,18 +853,21 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &Meta Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens), Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens), Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {} - Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => { + Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. } => { stdx::never!("metavariable expression in lhs found"); } } } } impl MetaTemplate { - fn iter_delimited(&self, delimited: Option>) -> OpDelimitedIter<'_, S> { + fn iter_delimited_with(&self, delimiter: tt::Delimiter) -> OpDelimitedIter<'_, S> { + OpDelimitedIter { inner: &self.0, idx: 0, delimited: delimiter } + } + fn iter_delimited(&self, span: tt::DelimSpan) -> OpDelimitedIter<'_, S> { OpDelimitedIter { inner: &self.0, idx: 0, - delimited: delimited.unwrap_or(tt::Delimiter::DUMMY_INVISIBLE), + delimited: tt::Delimiter::invisible_delim_spanned(span), } } } @@ -958,11 +963,13 @@ impl TtIter<'_, S> { self.expect_lifetime() } else { let puncts = self.expect_glued_punct()?; + let delimiter = tt::Delimiter { + open: puncts.first().unwrap().span, + close: puncts.last().unwrap().span, + kind: tt::DelimiterKind::Invisible, + }; let token_trees = puncts.into_iter().map(|p| tt::Leaf::Punct(p).into()).collect(); - Ok(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::dummy_invisible(), - token_trees, - })) + Ok(tt::TokenTree::Subtree(tt::Subtree { delimiter, token_trees })) } } else { self.next().ok_or(()).cloned() @@ -977,7 +984,11 @@ impl TtIter<'_, S> { let ident = self.expect_ident_or_underscore()?; Ok(tt::Subtree { - delimiter: tt::Delimiter::dummy_invisible(), + delimiter: tt::Delimiter { + open: punct.span, + close: ident.span, + kind: tt::DelimiterKind::Invisible, + }, token_trees: vec![ tt::Leaf::Punct(*punct).into(), tt::Leaf::Ident(ident.clone()).into(), diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index 7a3e8653c28f..6e79cdaa0b9d 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs @@ -59,12 +59,12 @@ impl Bindings { token_trees: token_trees.clone(), }; Ok(match f { - Fragment::Tokens(_) => unreachable!(), + Fragment::Tokens(_) | Fragment::Empty => unreachable!(), Fragment::Expr(_) => Fragment::Expr, Fragment::Path(_) => Fragment::Path, }(subtree)) } - Binding::Fragment(it @ Fragment::Tokens(_)) => Ok(it.clone()), + Binding::Fragment(it @ (Fragment::Tokens(_) | Fragment::Empty)) => Ok(it.clone()), // emit some reasonable default expansion for missing bindings, // this gives better recovery than emitting the `$fragment-name` verbatim Binding::Missing(it) => Ok({ @@ -87,10 +87,7 @@ impl Bindings { })), // FIXME: Meta and Item should get proper defaults MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => { - Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::DUMMY_INVISIBLE, - token_trees: vec![], - })) + Fragment::Empty } MetaVarKind::Path | MetaVarKind::Ty @@ -131,8 +128,10 @@ pub(super) fn transcribe( template: &MetaTemplate, bindings: &Bindings, marker: impl Fn(&mut S) + Copy, + new_meta_vars: bool, + call_site: S, ) -> ExpandResult> { - let mut ctx = ExpandCtx { bindings, nesting: Vec::new() }; + let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), new_meta_vars, call_site }; let mut arena: Vec> = Vec::new(); expand_subtree(&mut ctx, template, None, &mut arena, marker) } @@ -152,6 +151,8 @@ struct NestingState { struct ExpandCtx<'a, S> { bindings: &'a Bindings, nesting: Vec, + new_meta_vars: bool, + call_site: S, } fn expand_subtree( @@ -206,13 +207,13 @@ fn expand_subtree( Op::Var { name, id, .. } => { let ExpandResult { value: fragment, err: e } = expand_var(ctx, name, *id, marker); err = err.or(e); - push_fragment(arena, fragment); + push_fragment(ctx, arena, fragment); } Op::Repeat { tokens: subtree, kind, separator } => { let ExpandResult { value: fragment, err: e } = expand_repeat(ctx, subtree, *kind, separator, arena, marker); err = err.or(e); - push_fragment(arena, fragment) + push_fragment(ctx, arena, fragment) } Op::Ignore { name, id } => { // Expand the variable, but ignore the result. This registers the repetition count. @@ -225,8 +226,20 @@ fn expand_subtree( arena.push( tt::Leaf::Literal(tt::Literal { text: index.to_string().into(), - // FIXME - span: S::DUMMY, + span: ctx.call_site, + }) + .into(), + ); + } + Op::Length { depth } => { + let length = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |_nest| { + // FIXME: to be implemented + 0 + }); + arena.push( + tt::Leaf::Literal(tt::Literal { + text: length.to_string().into(), + span: ctx.call_site, }) .into(), ); @@ -268,7 +281,13 @@ fn expand_subtree( } } - let c = match count(ctx, binding, 0, *depth) { + let res = if ctx.new_meta_vars { + count(ctx, binding, 0, depth.unwrap_or(0)) + } else { + count_old(ctx, binding, 0, *depth) + }; + + let c = match res { Ok(c) => c, Err(e) => { // XXX: It *might* make sense to emit a dummy integer value like `0` here. @@ -285,8 +304,7 @@ fn expand_subtree( arena.push( tt::Leaf::Literal(tt::Literal { text: c.to_string().into(), - // FIXME - span: S::DUMMY, + span: ctx.call_site, }) .into(), ); @@ -297,7 +315,7 @@ fn expand_subtree( let tts = arena.drain(start_elements..).collect(); ExpandResult { value: tt::Subtree { - delimiter: delimiter.unwrap_or_else(tt::Delimiter::dummy_invisible), + delimiter: delimiter.unwrap_or_else(|| tt::Delimiter::invisible_spanned(ctx.call_site)), token_trees: tts, }, err, @@ -330,7 +348,7 @@ fn expand_var( // ``` // We just treat it a normal tokens let tt = tt::Subtree { - delimiter: tt::Delimiter::DUMMY_INVISIBLE, + delimiter: tt::Delimiter::invisible_spanned(id), token_trees: vec![ tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id }) .into(), @@ -342,10 +360,8 @@ fn expand_var( } Err(e) => ExpandResult { value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty(tt::DelimSpan { - // FIXME - open: S::DUMMY, - // FIXME - close: S::DUMMY, + open: ctx.call_site, + close: ctx.call_site, }))), err: Some(e), }, @@ -389,7 +405,7 @@ fn expand_repeat( return ExpandResult { value: Fragment::Tokens( tt::Subtree { - delimiter: tt::Delimiter::dummy_invisible(), + delimiter: tt::Delimiter::invisible_spanned(ctx.call_site), token_trees: vec![], } .into(), @@ -403,7 +419,7 @@ fn expand_repeat( continue; } - t.delimiter = tt::Delimiter::DUMMY_INVISIBLE; + t.delimiter.kind = tt::DelimiterKind::Invisible; push_subtree(&mut buf, t); if let Some(sep) = separator { @@ -437,7 +453,11 @@ fn expand_repeat( // Check if it is a single token subtree without any delimiter // e.g {Delimiter:None> ['>'] /Delimiter:None>} - let tt = tt::Subtree { delimiter: tt::Delimiter::DUMMY_INVISIBLE, token_trees: buf }.into(); + let tt = tt::Subtree { + delimiter: tt::Delimiter::invisible_spanned(ctx.call_site), + token_trees: buf, + } + .into(); if RepeatKind::OneOrMore == kind && counter == 0 { return ExpandResult { @@ -448,14 +468,19 @@ fn expand_repeat( ExpandResult { value: Fragment::Tokens(tt), err } } -fn push_fragment(buf: &mut Vec>, fragment: Fragment) { +fn push_fragment( + ctx: &ExpandCtx<'_, S>, + buf: &mut Vec>, + fragment: Fragment, +) { match fragment { Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt), Fragment::Expr(sub) => { push_subtree(buf, sub); } - Fragment::Path(tt) => fix_up_and_push_path_tt(buf, tt), + Fragment::Path(tt) => fix_up_and_push_path_tt(ctx, buf, tt), Fragment::Tokens(tt) => buf.push(tt), + Fragment::Empty => (), } } @@ -469,7 +494,11 @@ fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { /// Inserts the path separator `::` between an identifier and its following generic /// argument list, and then pushes into the buffer. See [`Fragment::Path`] for why /// we need this fixup. -fn fix_up_and_push_path_tt(buf: &mut Vec>, subtree: tt::Subtree) { +fn fix_up_and_push_path_tt( + ctx: &ExpandCtx<'_, S>, + buf: &mut Vec>, + subtree: tt::Subtree, +) { stdx::always!(matches!(subtree.delimiter.kind, tt::DelimiterKind::Invisible)); let mut prev_was_ident = false; // Note that we only need to fix up the top-level `TokenTree`s because the @@ -486,8 +515,7 @@ fn fix_up_and_push_path_tt(buf: &mut Vec>, subtree: tt tt::Leaf::Punct(tt::Punct { char: ':', spacing: tt::Spacing::Joint, - // FIXME - span: S::DUMMY, + span: ctx.call_site, }) .into(), ); @@ -495,8 +523,7 @@ fn fix_up_and_push_path_tt(buf: &mut Vec>, subtree: tt tt::Leaf::Punct(tt::Punct { char: ':', spacing: tt::Spacing::Alone, - // FIXME - span: S::DUMMY, + span: ctx.call_site, }) .into(), ); @@ -510,6 +537,25 @@ fn fix_up_and_push_path_tt(buf: &mut Vec>, subtree: tt /// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth /// defined by the metavar expression. fn count( + ctx: &ExpandCtx<'_, S>, + binding: &Binding, + depth_curr: usize, + depth_max: usize, +) -> Result { + match binding { + Binding::Nested(bs) => { + if depth_curr == depth_max { + Ok(bs.len()) + } else { + bs.iter().map(|b| count(ctx, b, depth_curr + 1, depth_max)).sum() + } + } + Binding::Empty => Ok(0), + Binding::Fragment(_) | Binding::Missing(_) => Ok(1), + } +} + +fn count_old( ctx: &ExpandCtx<'_, S>, binding: &Binding, our_depth: usize, @@ -517,9 +563,9 @@ fn count( ) -> Result { match binding { Binding::Nested(bs) => match count_depth { - None => bs.iter().map(|b| count(ctx, b, our_depth + 1, None)).sum(), + None => bs.iter().map(|b| count_old(ctx, b, our_depth + 1, None)).sum(), Some(0) => Ok(bs.len()), - Some(d) => bs.iter().map(|b| count(ctx, b, our_depth + 1, Some(d - 1))).sum(), + Some(d) => bs.iter().map(|b| count_old(ctx, b, our_depth + 1, Some(d - 1))).sum(), }, Binding::Empty => Ok(0), Binding::Fragment(_) | Binding::Missing(_) => { diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 9331798589fc..2622d7eac10e 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -16,7 +16,6 @@ mod to_parser_input; #[cfg(test)] mod benchmark; -mod token_map; use stdx::impl_from; use tt::Span; @@ -30,15 +29,12 @@ use crate::{ // FIXME: we probably should re-think `token_tree_to_syntax_node` interfaces pub use ::parser::TopEntryPoint; -pub use tt::{Delimiter, DelimiterKind, Punct, SyntaxContext}; +pub use tt::{Delimiter, DelimiterKind, Punct}; -pub use crate::{ - syntax_bridge::{ - parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span, - syntax_node_to_token_tree, syntax_node_to_token_tree_modified, token_tree_to_syntax_node, - SpanMapper, - }, - token_map::SpanMap, +pub use crate::syntax_bridge::{ + parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span, + syntax_node_to_token_tree, syntax_node_to_token_tree_modified, token_tree_to_syntax_node, + SpanMapper, }; pub use crate::syntax_bridge::dummy_test_span_utils::*; @@ -151,7 +147,12 @@ impl DeclarativeMacro { } /// The old, `macro_rules! m {}` flavor. - pub fn parse_macro_rules(tt: &tt::Subtree, is_2021: bool) -> DeclarativeMacro { + pub fn parse_macro_rules( + tt: &tt::Subtree, + is_2021: bool, + // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) + new_meta_vars: bool, + ) -> DeclarativeMacro { // Note: this parsing can be implemented using mbe machinery itself, by // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing // manually seems easier. @@ -160,7 +161,7 @@ impl DeclarativeMacro { let mut err = None; while src.len() > 0 { - let rule = match Rule::parse(&mut src, true) { + let rule = match Rule::parse(&mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -187,7 +188,12 @@ impl DeclarativeMacro { } /// The new, unstable `macro m {}` flavor. - pub fn parse_macro2(tt: &tt::Subtree, is_2021: bool) -> DeclarativeMacro { + pub fn parse_macro2( + tt: &tt::Subtree, + is_2021: bool, + // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) + new_meta_vars: bool, + ) -> DeclarativeMacro { let mut src = TtIter::new(tt); let mut rules = Vec::new(); let mut err = None; @@ -195,7 +201,7 @@ impl DeclarativeMacro { if tt::DelimiterKind::Brace == tt.delimiter.kind { cov_mark::hit!(parse_macro_def_rules); while src.len() > 0 { - let rule = match Rule::parse(&mut src, true) { + let rule = match Rule::parse(&mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -214,7 +220,7 @@ impl DeclarativeMacro { } } else { cov_mark::hit!(parse_macro_def_simple); - match Rule::parse(&mut src, false) { + match Rule::parse(&mut src, false, new_meta_vars) { Ok(rule) => { if src.len() != 0 { err = Some(Box::new(ParseError::expected("remaining tokens in macro def"))); @@ -245,13 +251,19 @@ impl DeclarativeMacro { &self, tt: &tt::Subtree, marker: impl Fn(&mut S) + Copy, + new_meta_vars: bool, + call_site: S, ) -> ExpandResult> { - expander::expand_rules(&self.rules, &tt, marker, self.is_2021) + expander::expand_rules(&self.rules, &tt, marker, self.is_2021, new_meta_vars, call_site) } } impl Rule { - fn parse(src: &mut TtIter<'_, S>, expect_arrow: bool) -> Result { + fn parse( + src: &mut TtIter<'_, S>, + expect_arrow: bool, + new_meta_vars: bool, + ) -> Result { let lhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; if expect_arrow { src.expect_char('=').map_err(|()| ParseError::expected("expected `=`"))?; @@ -260,7 +272,7 @@ impl Rule { let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; let lhs = MetaTemplate::parse_pattern(lhs)?; - let rhs = MetaTemplate::parse_template(rhs)?; + let rhs = MetaTemplate::parse_template(rhs, new_meta_vars)?; Ok(crate::Rule { lhs, rhs }) } diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 00ba35377a42..afdbbef23147 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs @@ -25,23 +25,26 @@ pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); impl MetaTemplate { pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result { - MetaTemplate::parse(pattern, Mode::Pattern) + MetaTemplate::parse(pattern, Mode::Pattern, false) } - pub(crate) fn parse_template(template: &tt::Subtree) -> Result { - MetaTemplate::parse(template, Mode::Template) + pub(crate) fn parse_template( + template: &tt::Subtree, + new_meta_vars: bool, + ) -> Result { + MetaTemplate::parse(template, Mode::Template, new_meta_vars) } pub(crate) fn iter(&self) -> impl Iterator> { self.0.iter() } - fn parse(tt: &tt::Subtree, mode: Mode) -> Result { + fn parse(tt: &tt::Subtree, mode: Mode, new_meta_vars: bool) -> Result { let mut src = TtIter::new(tt); let mut res = Vec::new(); while let Some(first) = src.peek_n(0) { - let op = next_op(first, &mut src, mode)?; + let op = next_op(first, &mut src, mode, new_meta_vars)?; res.push(op); } @@ -51,12 +54,35 @@ impl MetaTemplate { #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum Op { - Var { name: SmolStr, kind: Option, id: S }, - Ignore { name: SmolStr, id: S }, - Index { depth: usize }, - Count { name: SmolStr, depth: Option }, - Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option> }, - Subtree { tokens: MetaTemplate, delimiter: tt::Delimiter }, + Var { + name: SmolStr, + kind: Option, + id: S, + }, + Ignore { + name: SmolStr, + id: S, + }, + Index { + depth: usize, + }, + Length { + depth: usize, + }, + Count { + name: SmolStr, + // FIXME: `usize`` once we drop support for 1.76 + depth: Option, + }, + Repeat { + tokens: MetaTemplate, + kind: RepeatKind, + separator: Option>, + }, + Subtree { + tokens: MetaTemplate, + delimiter: tt::Delimiter, + }, Literal(tt::Literal), Punct(SmallVec<[tt::Punct; 3]>), Ident(tt::Ident), @@ -122,6 +148,7 @@ fn next_op( first_peeked: &tt::TokenTree, src: &mut TtIter<'_, S>, mode: Mode, + new_meta_vars: bool, ) -> Result, ParseError> { let res = match first_peeked { tt::TokenTree::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => { @@ -135,14 +162,14 @@ fn next_op( tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind { tt::DelimiterKind::Parenthesis => { let (separator, kind) = parse_repeat(src)?; - let tokens = MetaTemplate::parse(subtree, mode)?; + let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; Op::Repeat { tokens, separator, kind } } tt::DelimiterKind::Brace => match mode { Mode::Template => { - parse_metavar_expr(&mut TtIter::new(subtree)).map_err(|()| { - ParseError::unexpected("invalid metavariable expression") - })? + parse_metavar_expr(new_meta_vars, &mut TtIter::new(subtree)).map_err( + |()| ParseError::unexpected("invalid metavariable expression"), + )? } Mode::Pattern => { return Err(ParseError::unexpected( @@ -206,7 +233,7 @@ fn next_op( tt::TokenTree::Subtree(subtree) => { src.next().expect("first token already peeked"); - let tokens = MetaTemplate::parse(subtree, mode)?; + let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; Op::Subtree { tokens, delimiter: subtree.delimiter } } }; @@ -287,7 +314,7 @@ fn parse_repeat( Err(ParseError::InvalidRepeat) } -fn parse_metavar_expr(src: &mut TtIter<'_, S>) -> Result, ()> { +fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, S>) -> Result, ()> { let func = src.expect_ident()?; let args = src.expect_subtree()?; @@ -299,14 +326,19 @@ fn parse_metavar_expr(src: &mut TtIter<'_, S>) -> Result, ()> { let op = match &*func.text { "ignore" => { + if new_meta_vars { + args.expect_dollar()?; + } let ident = args.expect_ident()?; Op::Ignore { name: ident.text.clone(), id: ident.span } } "index" => Op::Index { depth: parse_depth(&mut args)? }, + "length" => Op::Length { depth: parse_depth(&mut args)? }, "count" => { + if new_meta_vars { + args.expect_dollar()?; + } let ident = args.expect_ident()?; - // `${count(t)}` and `${count(t,)}` have different meanings. Not sure if this is a bug - // but that's how it's implemented in rustc as of this writing. See rust-lang/rust#111904. let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None }; Op::Count { name: ident.text.clone(), depth } } diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index b89bfd74a6e0..8fa04ab983f0 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -1,6 +1,7 @@ //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`]. use rustc_hash::{FxHashMap, FxHashSet}; +use span::{SpanAnchor, SpanData, SpanMap}; use stdx::{never, non_empty_vec::NonEmptyVec}; use syntax::{ ast::{self, make::tokens::doc_comment}, @@ -10,10 +11,10 @@ use syntax::{ }; use tt::{ buffer::{Cursor, TokenBuffer}, - Span, SpanData, SyntaxContext, + Span, }; -use crate::{to_parser_input::to_parser_input, tt_iter::TtIter, SpanMap}; +use crate::{to_parser_input::to_parser_input, tt_iter::TtIter}; #[cfg(test)] mod tests; @@ -36,66 +37,70 @@ impl> SpanMapper for &SM { /// Dummy things for testing where spans don't matter. pub(crate) mod dummy_test_span_utils { + use super::*; - pub type DummyTestSpanData = tt::SpanData; - pub const DUMMY: DummyTestSpanData = DummyTestSpanData::DUMMY; + pub type DummyTestSpanData = span::SpanData; + pub const DUMMY: DummyTestSpanData = span::SpanData { + range: TextRange::empty(TextSize::new(0)), + anchor: span::SpanAnchor { + file_id: span::FileId::BOGUS, + ast_id: span::ROOT_ERASED_FILE_AST_ID, + }, + ctx: DummyTestSyntaxContext, + }; - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub struct DummyTestSpanAnchor; - impl tt::SpanAnchor for DummyTestSpanAnchor { - const DUMMY: Self = DummyTestSpanAnchor; - } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct DummyTestSyntaxContext; - impl SyntaxContext for DummyTestSyntaxContext { - const DUMMY: Self = DummyTestSyntaxContext; - } pub struct DummyTestSpanMap; - impl SpanMapper> for DummyTestSpanMap { - fn span_for( - &self, - range: syntax::TextRange, - ) -> tt::SpanData { - tt::SpanData { range, anchor: DummyTestSpanAnchor, ctx: DummyTestSyntaxContext } + impl SpanMapper> for DummyTestSpanMap { + fn span_for(&self, range: syntax::TextRange) -> span::SpanData { + span::SpanData { + range, + anchor: span::SpanAnchor { + file_id: span::FileId::BOGUS, + ast_id: span::ROOT_ERASED_FILE_AST_ID, + }, + ctx: DummyTestSyntaxContext, + } } } } /// Converts a syntax tree to a [`tt::Subtree`] using the provided span map to populate the /// subtree's spans. -pub fn syntax_node_to_token_tree( +pub fn syntax_node_to_token_tree( node: &SyntaxNode, map: SpanMap, -) -> tt::Subtree> + span: SpanData, +) -> tt::Subtree> where - SpanData: Span, - Anchor: Copy, - Ctx: SyntaxContext, - SpanMap: SpanMapper>, + SpanData: Span, + Ctx: Copy, + SpanMap: SpanMapper>, { - let mut c = Converter::new(node, map, Default::default(), Default::default()); + let mut c = Converter::new(node, map, Default::default(), Default::default(), span); convert_tokens(&mut c) } /// Converts a syntax tree to a [`tt::Subtree`] using the provided span map to populate the /// subtree's spans. Additionally using the append and remove parameters, the additional tokens can /// be injected or hidden from the output. -pub fn syntax_node_to_token_tree_modified( +pub fn syntax_node_to_token_tree_modified( node: &SyntaxNode, map: SpanMap, - append: FxHashMap>>>, + append: FxHashMap>>>, remove: FxHashSet, -) -> tt::Subtree> + call_site: SpanData, +) -> tt::Subtree> where - SpanMap: SpanMapper>, - SpanData: Span, - Anchor: Copy, - Ctx: SyntaxContext, + SpanMap: SpanMapper>, + SpanData: Span, + Ctx: Copy, { - let mut c = Converter::new(node, map, append, remove); + let mut c = Converter::new(node, map, append, remove, call_site); convert_tokens(&mut c) } @@ -113,14 +118,13 @@ where /// Converts a [`tt::Subtree`] back to a [`SyntaxNode`]. /// The produced `SpanMap` contains a mapping from the syntax nodes offsets to the subtree's spans. -pub fn token_tree_to_syntax_node( - tt: &tt::Subtree>, +pub fn token_tree_to_syntax_node( + tt: &tt::Subtree>, entry_point: parser::TopEntryPoint, -) -> (Parse, SpanMap>) +) -> (Parse, SpanMap>) where - SpanData: Span, - Anchor: Copy, - Ctx: SyntaxContext, + SpanData: Span, + Ctx: Copy, { let buffer = match tt { tt::Subtree { @@ -150,21 +154,20 @@ where /// Convert a string to a `TokenTree`. The spans of the subtree will be anchored to the provided /// anchor with the given context. -pub fn parse_to_token_tree( - anchor: Anchor, +pub fn parse_to_token_tree( + anchor: SpanAnchor, ctx: Ctx, text: &str, -) -> Option>> +) -> Option>> where - SpanData: Span, - Anchor: Copy, - Ctx: SyntaxContext, + SpanData: Span, + Ctx: Copy, { let lexed = parser::LexedStr::new(text); if lexed.errors().next().is_some() { return None; } - let mut conv = RawConverter { lexed, pos: 0, anchor, ctx }; + let mut conv = RawConverter { lexed, anchor, pos: 0, ctx }; Some(convert_tokens(&mut conv)) } @@ -182,7 +185,11 @@ where } /// Split token tree with separate expr: $($e:expr)SEP* -pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec> { +pub fn parse_exprs_with_sep( + tt: &tt::Subtree, + sep: char, + span: S, +) -> Vec> { if tt.token_trees.is_empty() { return Vec::new(); } @@ -195,7 +202,7 @@ pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec break, - Some(tt) => tt.subtree_or_wrap(), + Some(tt) => tt.subtree_or_wrap(tt::DelimSpan { open: span, close: span }), }); let mut fork = iter.clone(); @@ -207,7 +214,7 @@ pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec, S: Span, { - let entry = tt::Subtree { delimiter: tt::Delimiter::DUMMY_INVISIBLE, token_trees: vec![] }; + let entry = tt::Subtree { + delimiter: tt::Delimiter::invisible_spanned(conv.call_site()), + token_trees: vec![], + }; let mut stack = NonEmptyVec::new(entry); while let Some((token, abs_range)) = conv.bump() { @@ -401,9 +411,20 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { text = &text[0..text.len() - 2]; } - // Quote the string + let mut num_of_hashes = 0; + let mut count = 0; + for ch in text.chars() { + count = match ch { + '"' => 1, + '#' if count > 0 => count + 1, + _ => 0, + }; + num_of_hashes = num_of_hashes.max(count); + } + + // Quote raw string with delimiters // Note that `tt::Literal` expect an escaped string - let text = format!("\"{}\"", text.escape_debug()); + let text = format!("r{delim}\"{text}\"{delim}", delim = "#".repeat(num_of_hashes)); text.into() } @@ -450,10 +471,10 @@ fn convert_doc_comment( } /// A raw token (straight from lexer) converter -struct RawConverter<'a, Anchor, Ctx> { +struct RawConverter<'a, Ctx> { lexed: parser::LexedStr<'a>, pos: usize, - anchor: Anchor, + anchor: SpanAnchor, ctx: Ctx, } /// A raw token (straight from lexer) converter that gives every token the same span. @@ -485,18 +506,20 @@ trait TokenConverter: Sized { fn peek(&self) -> Option; fn span_for(&self, range: TextRange) -> S; + + fn call_site(&self) -> S; } -impl SrcToken, S> for usize { - fn kind(&self, ctx: &RawConverter<'_, Anchor, Ctx>) -> SyntaxKind { +impl SrcToken, S> for usize { + fn kind(&self, ctx: &RawConverter<'_, Ctx>) -> SyntaxKind { ctx.lexed.kind(*self) } - fn to_char(&self, ctx: &RawConverter<'_, Anchor, Ctx>) -> Option { + fn to_char(&self, ctx: &RawConverter<'_, Ctx>) -> Option { ctx.lexed.text(*self).chars().next() } - fn to_text(&self, ctx: &RawConverter<'_, Anchor, Ctx>) -> SmolStr { + fn to_text(&self, ctx: &RawConverter<'_, Ctx>) -> SmolStr { ctx.lexed.text(*self).into() } } @@ -515,18 +538,17 @@ impl SrcToken, S> for usize { } } -impl TokenConverter> - for RawConverter<'_, Anchor, Ctx> +impl TokenConverter> for RawConverter<'_, Ctx> where - SpanData: Span, + SpanData: Span, { type Token = usize; fn convert_doc_comment( &self, &token: &usize, - span: SpanData, - ) -> Option>>> { + span: SpanData, + ) -> Option>>> { let text = self.lexed.text(token); convert_doc_comment(&doc_comment(text), span) } @@ -550,9 +572,13 @@ where Some(self.pos) } - fn span_for(&self, range: TextRange) -> SpanData { + fn span_for(&self, range: TextRange) -> SpanData { SpanData { range, anchor: self.anchor, ctx: self.ctx } } + + fn call_site(&self) -> SpanData { + SpanData { range: TextRange::empty(0.into()), anchor: self.anchor, ctx: self.ctx } + } } impl TokenConverter for StaticRawConverter<'_, S> @@ -588,6 +614,10 @@ where fn span_for(&self, _: TextRange) -> S { self.span } + + fn call_site(&self) -> S { + self.span + } } struct Converter { @@ -600,6 +630,7 @@ struct Converter { map: SpanMap, append: FxHashMap>>, remove: FxHashSet, + call_site: S, } impl Converter { @@ -608,6 +639,7 @@ impl Converter { map: SpanMap, append: FxHashMap>>, remove: FxHashSet, + call_site: S, ) -> Self { let mut this = Converter { current: None, @@ -617,6 +649,7 @@ impl Converter { map, append, remove, + call_site, current_leafs: vec![], }; let first = this.next_token(); @@ -776,24 +809,27 @@ where fn span_for(&self, range: TextRange) -> S { self.map.span_for(range) } + fn call_site(&self) -> S { + self.call_site + } } -struct TtTreeSink<'a, Anchor, Ctx> +struct TtTreeSink<'a, Ctx> where - SpanData: Span, + SpanData: Span, { buf: String, - cursor: Cursor<'a, SpanData>, + cursor: Cursor<'a, SpanData>, text_pos: TextSize, inner: SyntaxTreeBuilder, - token_map: SpanMap>, + token_map: SpanMap>, } -impl<'a, Anchor, Ctx> TtTreeSink<'a, Anchor, Ctx> +impl<'a, Ctx> TtTreeSink<'a, Ctx> where - SpanData: Span, + SpanData: Span, { - fn new(cursor: Cursor<'a, SpanData>) -> Self { + fn new(cursor: Cursor<'a, SpanData>) -> Self { TtTreeSink { buf: String::new(), cursor, @@ -803,7 +839,7 @@ where } } - fn finish(mut self) -> (Parse, SpanMap>) { + fn finish(mut self) -> (Parse, SpanMap>) { self.token_map.finish(); (self.inner.finish(), self.token_map) } @@ -821,9 +857,9 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> { Some(&texts[idx..texts.len() - (1 - idx)]) } -impl TtTreeSink<'_, Anchor, Ctx> +impl TtTreeSink<'_, Ctx> where - SpanData: Span, + SpanData: Span, { /// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween. /// This occurs when a float literal is used as a field access. diff --git a/crates/mbe/src/syntax_bridge/tests.rs b/crates/mbe/src/syntax_bridge/tests.rs index bd8187a148a5..e5569138dbf2 100644 --- a/crates/mbe/src/syntax_bridge/tests.rs +++ b/crates/mbe/src/syntax_bridge/tests.rs @@ -7,11 +7,11 @@ use tt::{ Leaf, Punct, Spacing, }; -use crate::{syntax_node_to_token_tree, DummyTestSpanData, DummyTestSpanMap}; +use crate::{syntax_node_to_token_tree, DummyTestSpanData, DummyTestSpanMap, DUMMY}; fn check_punct_spacing(fixture: &str) { let source_file = ast::SourceFile::parse(fixture).ok().unwrap(); - let subtree = syntax_node_to_token_tree(source_file.syntax(), DummyTestSpanMap); + let subtree = syntax_node_to_token_tree(source_file.syntax(), DummyTestSpanMap, DUMMY); let mut annotations: HashMap<_, _> = extract_annotations(fixture) .into_iter() .map(|(range, annotation)| { diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs index 40e8a2385f46..71513ef43917 100644 --- a/crates/mbe/src/tt_iter.rs +++ b/crates/mbe/src/tt_iter.rs @@ -51,6 +51,13 @@ impl<'a, S: Span> TtIter<'a, S> { } } + pub(crate) fn expect_dollar(&mut self) -> Result<(), ()> { + match self.expect_leaf()? { + tt::Leaf::Punct(tt::Punct { char: '$', .. }) => Ok(()), + _ => Err(()), + } + } + pub(crate) fn expect_ident(&mut self) -> Result<&'a tt::Ident, ()> { match self.expect_leaf()? { tt::Leaf::Ident(it) if it.text != "_" => Ok(it), @@ -169,10 +176,10 @@ impl<'a, S: Span> TtIter<'a, S> { } self.inner = self.inner.as_slice()[res.len()..].iter(); - let res = match res.len() { - 0 | 1 => res.pop(), - _ => Some(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::DUMMY_INVISIBLE, + let res = match &*res { + [] | [_] => res.pop(), + [first, ..] => Some(tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter::invisible_spanned(first.first_span()), token_trees: res, })), }; diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index efb326323f91..0c63484634be 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -25,3 +25,6 @@ sourcegen.workspace = true [features] in-rust-tree = ["rustc-dependencies/in-rust-tree"] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml index 28b54be5212f..3d8752b5a829 100644 --- a/crates/paths/Cargo.toml +++ b/crates/paths/Cargo.toml @@ -16,3 +16,6 @@ doctest = false # serde-derive crate. Even though we don't activate the derive feature here, # someone else in the crate graph certainly does! # serde.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index 2cbbc9489a29..49a0979f4f5c 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -33,7 +33,11 @@ tt.workspace = true stdx.workspace = true profile.workspace = true text-size.workspace = true +span.workspace = true # Ideally this crate would not depend on salsa things, but we need span information here which wraps # InternIds for the syntax context base-db.workspace = true la-arena.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index f697ecd3518f..a87becd63e28 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -11,16 +11,19 @@ pub mod msg; mod process; mod version; -use base_db::span::SpanData; use indexmap::IndexSet; use paths::AbsPathBuf; +use span::Span; use std::{fmt, io, sync::Mutex}; use triomphe::Arc; use serde::{Deserialize, Serialize}; use crate::{ - msg::{ExpandMacro, ExpnGlobals, FlatTree, PanicMessage, HAS_GLOBAL_SPANS}, + msg::{ + deserialize_span_data_index_map, flat::serialize_span_data_index_map, ExpandMacro, + ExpnGlobals, FlatTree, PanicMessage, HAS_GLOBAL_SPANS, RUST_ANALYZER_SPAN_SUPPORT, + }, process::ProcMacroProcessSrv, }; @@ -136,13 +139,13 @@ impl ProcMacro { pub fn expand( &self, - subtree: &tt::Subtree, - attr: Option<&tt::Subtree>, + subtree: &tt::Subtree, + attr: Option<&tt::Subtree>, env: Vec<(String, String)>, - def_site: SpanData, - call_site: SpanData, - mixed_site: SpanData, - ) -> Result, PanicMessage>, ServerError> { + def_site: Span, + call_site: Span, + mixed_site: Span, + ) -> Result, PanicMessage>, ServerError> { let version = self.process.lock().unwrap_or_else(|e| e.into_inner()).version(); let current_dir = env .iter() @@ -166,6 +169,11 @@ impl ProcMacro { call_site, mixed_site, }, + span_data_table: if version >= RUST_ANALYZER_SPAN_SUPPORT { + serialize_span_data_index_map(&span_data_table) + } else { + Vec::new() + }, }; let response = self @@ -178,9 +186,14 @@ impl ProcMacro { msg::Response::ExpandMacro(it) => { Ok(it.map(|tree| FlatTree::to_subtree_resolved(tree, version, &span_data_table))) } - msg::Response::ListMacros(..) | msg::Response::ApiVersionCheck(..) => { - Err(ServerError { message: "unexpected response".to_string(), io: None }) - } + msg::Response::ExpandMacroExtended(it) => Ok(it.map(|resp| { + FlatTree::to_subtree_resolved( + resp.tree, + version, + &deserialize_span_data_index_map(&resp.span_data_table), + ) + })), + _ => Err(ServerError { message: "unexpected response".to_string(), io: None }), } } } diff --git a/crates/proc-macro-api/src/msg.rs b/crates/proc-macro-api/src/msg.rs index 1d3e45aff385..557ddba5c78f 100644 --- a/crates/proc-macro-api/src/msg.rs +++ b/crates/proc-macro-api/src/msg.rs @@ -10,28 +10,63 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::ProcMacroKind; -pub use crate::msg::flat::{FlatTree, TokenId}; +pub use crate::msg::flat::{ + deserialize_span_data_index_map, serialize_span_data_index_map, FlatTree, SpanDataIndexMap, + TokenId, +}; // The versions of the server protocol pub const NO_VERSION_CHECK_VERSION: u32 = 0; pub const VERSION_CHECK_VERSION: u32 = 1; pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2; pub const HAS_GLOBAL_SPANS: u32 = 3; +pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4; -pub const CURRENT_API_VERSION: u32 = HAS_GLOBAL_SPANS; +pub const CURRENT_API_VERSION: u32 = RUST_ANALYZER_SPAN_SUPPORT; #[derive(Debug, Serialize, Deserialize)] pub enum Request { + /// Since [`NO_VERSION_CHECK_VERSION`] ListMacros { dylib_path: PathBuf }, + /// Since [`NO_VERSION_CHECK_VERSION`] ExpandMacro(ExpandMacro), + /// Since [`VERSION_CHECK_VERSION`] ApiVersionCheck {}, + /// Since [`RUST_ANALYZER_SPAN_SUPPORT`] + SetConfig(ServerConfig), +} + +#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)] +pub enum SpanMode { + #[default] + Id, + RustAnalyzer, } #[derive(Debug, Serialize, Deserialize)] pub enum Response { + /// Since [`NO_VERSION_CHECK_VERSION`] ListMacros(Result, String>), + /// Since [`NO_VERSION_CHECK_VERSION`] ExpandMacro(Result), + /// Since [`NO_VERSION_CHECK_VERSION`] ApiVersionCheck(u32), + /// Since [`RUST_ANALYZER_SPAN_SUPPORT`] + SetConfig(ServerConfig), + /// Since [`RUST_ANALYZER_SPAN_SUPPORT`] + ExpandMacroExtended(Result), +} + +#[derive(Debug, Serialize, Deserialize, Default)] +#[serde(default)] +pub struct ServerConfig { + pub span_mode: SpanMode, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ExpandMacroExtended { + pub tree: FlatTree, + pub span_data_table: Vec, } #[derive(Debug, Serialize, Deserialize)] @@ -64,9 +99,12 @@ pub struct ExpandMacro { #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")] #[serde(default)] pub has_global_spans: ExpnGlobals, + #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] + pub span_data_table: Vec, } -#[derive(Default, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)] pub struct ExpnGlobals { #[serde(skip_serializing)] #[serde(default)] @@ -136,29 +174,27 @@ fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> { #[cfg(test)] mod tests { - use base_db::{ - span::{ErasedFileAstId, SpanAnchor, SpanData, SyntaxContextId}, - FileId, - }; + use base_db::FileId; use la_arena::RawIdx; + use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId}; use text_size::{TextRange, TextSize}; use tt::{Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, Subtree, TokenTree}; use super::*; - fn fixture_token_tree() -> Subtree { + fn fixture_token_tree() -> Subtree { let anchor = SpanAnchor { file_id: FileId::from_raw(0), ast_id: ErasedFileAstId::from_raw(RawIdx::from(0)), }; let mut subtree = Subtree { delimiter: Delimiter { - open: SpanData { + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: SyntaxContextId::ROOT, }, - close: SpanData { + close: Span { range: TextRange::empty(TextSize::new(13)), anchor, ctx: SyntaxContextId::ROOT, @@ -170,7 +206,7 @@ mod tests { subtree.token_trees.push(TokenTree::Leaf( Ident { text: "struct".into(), - span: SpanData { + span: Span { range: TextRange::at(TextSize::new(0), TextSize::of("struct")), anchor, ctx: SyntaxContextId::ROOT, @@ -181,7 +217,7 @@ mod tests { subtree.token_trees.push(TokenTree::Leaf( Ident { text: "Foo".into(), - span: SpanData { + span: Span { range: TextRange::at(TextSize::new(5), TextSize::of("Foo")), anchor, ctx: SyntaxContextId::ROOT, @@ -192,7 +228,7 @@ mod tests { subtree.token_trees.push(TokenTree::Leaf(Leaf::Literal(Literal { text: "Foo".into(), - span: SpanData { + span: Span { range: TextRange::at(TextSize::new(8), TextSize::of("Foo")), anchor, ctx: SyntaxContextId::ROOT, @@ -200,7 +236,7 @@ mod tests { }))); subtree.token_trees.push(TokenTree::Leaf(Leaf::Punct(Punct { char: '@', - span: SpanData { + span: Span { range: TextRange::at(TextSize::new(11), TextSize::of('@')), anchor, ctx: SyntaxContextId::ROOT, @@ -209,12 +245,12 @@ mod tests { }))); subtree.token_trees.push(TokenTree::Subtree(Subtree { delimiter: Delimiter { - open: SpanData { + open: Span { range: TextRange::at(TextSize::new(12), TextSize::of('{')), anchor, ctx: SyntaxContextId::ROOT, }, - close: SpanData { + close: Span { range: TextRange::at(TextSize::new(13), TextSize::of('}')), anchor, ctx: SyntaxContextId::ROOT, @@ -243,6 +279,7 @@ mod tests { call_site: 0, mixed_site: 0, }, + span_data_table: Vec::new(), }; let json = serde_json::to_string(&task).unwrap(); diff --git a/crates/proc-macro-api/src/msg/flat.rs b/crates/proc-macro-api/src/msg/flat.rs index 5835718628e5..8dfaba52625d 100644 --- a/crates/proc-macro-api/src/msg/flat.rs +++ b/crates/proc-macro-api/src/msg/flat.rs @@ -37,13 +37,46 @@ use std::collections::{HashMap, VecDeque}; -use base_db::span::SpanData; use indexmap::IndexSet; +use la_arena::RawIdx; use serde::{Deserialize, Serialize}; +use span::{ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId}; +use text_size::TextRange; use crate::msg::ENCODE_CLOSE_SPAN_VERSION; -type SpanDataIndexMap = IndexSet; +pub type SpanDataIndexMap = IndexSet; + +pub fn serialize_span_data_index_map(map: &SpanDataIndexMap) -> Vec { + map.iter() + .flat_map(|span| { + [ + span.anchor.file_id.index(), + span.anchor.ast_id.into_raw().into_u32(), + span.range.start().into(), + span.range.end().into(), + span.ctx.into_u32(), + ] + }) + .collect() +} + +pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap { + debug_assert!(map.len() % 5 == 0); + map.chunks_exact(5) + .map(|span| { + let &[file_id, ast_id, start, end, e] = span else { unreachable!() }; + Span { + anchor: SpanAnchor { + file_id: FileId::from_raw(file_id), + ast_id: ErasedFileAstId::from_raw(RawIdx::from_u32(ast_id)), + }, + range: TextRange::new(start.into(), end.into()), + ctx: SyntaxContextId::from_u32(e), + } + }) + .collect() +} #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct TokenId(pub u32); @@ -54,9 +87,7 @@ impl std::fmt::Debug for TokenId { } } -impl tt::Span for TokenId { - const DUMMY: Self = TokenId(!0); -} +impl tt::Span for TokenId {} #[derive(Serialize, Deserialize, Debug)] pub struct FlatTree { @@ -93,7 +124,7 @@ struct IdentRepr { impl FlatTree { pub fn new( - subtree: &tt::Subtree, + subtree: &tt::Subtree, version: u32, span_data_table: &mut SpanDataIndexMap, ) -> FlatTree { @@ -158,7 +189,7 @@ impl FlatTree { self, version: u32, span_data_table: &SpanDataIndexMap, - ) -> tt::Subtree { + ) -> tt::Subtree { Reader { subtree: if version >= ENCODE_CLOSE_SPAN_VERSION { read_vec(self.subtree, SubtreeRepr::read_with_close_span) @@ -281,13 +312,13 @@ impl IdentRepr { } } -trait Span: Copy { +trait InternableSpan: Copy { type Table; fn token_id_of(table: &mut Self::Table, s: Self) -> TokenId; fn span_for_token_id(table: &Self::Table, id: TokenId) -> Self; } -impl Span for TokenId { +impl InternableSpan for TokenId { type Table = (); fn token_id_of((): &mut Self::Table, token_id: Self) -> TokenId { token_id @@ -297,8 +328,8 @@ impl Span for TokenId { id } } -impl Span for SpanData { - type Table = IndexSet; +impl InternableSpan for Span { + type Table = IndexSet; fn token_id_of(table: &mut Self::Table, span: Self) -> TokenId { TokenId(table.insert_full(span).0 as u32) } @@ -307,7 +338,7 @@ impl Span for SpanData { } } -struct Writer<'a, 'span, S: Span> { +struct Writer<'a, 'span, S: InternableSpan> { work: VecDeque<(usize, &'a tt::Subtree)>, string_table: HashMap<&'a str, u32>, span_data_table: &'span mut S::Table, @@ -320,7 +351,7 @@ struct Writer<'a, 'span, S: Span> { text: Vec, } -impl<'a, 'span, S: Span> Writer<'a, 'span, S> { +impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> { fn write(&mut self, root: &'a tt::Subtree) { self.enqueue(root); while let Some((idx, subtree)) = self.work.pop_front() { @@ -393,7 +424,7 @@ impl<'a, 'span, S: Span> Writer<'a, 'span, S> { } } -struct Reader<'span, S: Span> { +struct Reader<'span, S: InternableSpan> { subtree: Vec, literal: Vec, punct: Vec, @@ -403,7 +434,7 @@ struct Reader<'span, S: Span> { span_data_table: &'span S::Table, } -impl<'span, S: Span> Reader<'span, S> { +impl<'span, S: InternableSpan> Reader<'span, S> { pub(crate) fn read(self) -> tt::Subtree { let mut res: Vec>> = vec![None; self.subtree.len()]; let read_span = |id| S::span_for_token_id(self.span_data_table, id); diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index 9a20fa63ed70..3494164c0675 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -9,7 +9,7 @@ use paths::{AbsPath, AbsPathBuf}; use stdx::JodChild; use crate::{ - msg::{Message, Request, Response, CURRENT_API_VERSION}, + msg::{Message, Request, Response, SpanMode, CURRENT_API_VERSION, RUST_ANALYZER_SPAN_SUPPORT}, ProcMacroKind, ServerError, }; @@ -19,6 +19,7 @@ pub(crate) struct ProcMacroProcessSrv { stdin: ChildStdin, stdout: BufReader, version: u32, + mode: SpanMode, } impl ProcMacroProcessSrv { @@ -27,7 +28,13 @@ impl ProcMacroProcessSrv { let mut process = Process::run(process_path.clone(), null_stderr)?; let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); - io::Result::Ok(ProcMacroProcessSrv { _process: process, stdin, stdout, version: 0 }) + io::Result::Ok(ProcMacroProcessSrv { + _process: process, + stdin, + stdout, + version: 0, + mode: SpanMode::Id, + }) }; let mut srv = create_srv(true)?; tracing::info!("sending version check"); @@ -43,6 +50,11 @@ impl ProcMacroProcessSrv { tracing::info!("got version {v}"); srv = create_srv(false)?; srv.version = v; + if srv.version > RUST_ANALYZER_SPAN_SUPPORT { + if let Ok(mode) = srv.enable_rust_analyzer_spans() { + srv.mode = mode; + } + } Ok(srv) } Err(e) => { @@ -62,9 +74,19 @@ impl ProcMacroProcessSrv { match response { Response::ApiVersionCheck(version) => Ok(version), - Response::ExpandMacro { .. } | Response::ListMacros { .. } => { - Err(ServerError { message: "unexpected response".to_string(), io: None }) - } + _ => Err(ServerError { message: "unexpected response".to_string(), io: None }), + } + } + + fn enable_rust_analyzer_spans(&mut self) -> Result { + let request = Request::SetConfig(crate::msg::ServerConfig { + span_mode: crate::msg::SpanMode::RustAnalyzer, + }); + let response = self.send_task(request)?; + + match response { + Response::SetConfig(crate::msg::ServerConfig { span_mode }) => Ok(span_mode), + _ => Err(ServerError { message: "unexpected response".to_string(), io: None }), } } @@ -78,9 +100,7 @@ impl ProcMacroProcessSrv { match response { Response::ListMacros(it) => Ok(it), - Response::ExpandMacro { .. } | Response::ApiVersionCheck { .. } => { - Err(ServerError { message: "unexpected response".to_string(), io: None }) - } + _ => Err(ServerError { message: "unexpected response".to_string(), io: None }), } } diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index 8f03c6ec7b57..980eab2696bf 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -18,3 +18,6 @@ sysroot-abi = ["proc-macro-srv/sysroot-abi"] [[bin]] name = "rust-analyzer-proc-macro-srv" path = "src/main.rs" + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index 50ce586fc429..000a526e9f99 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -39,10 +39,22 @@ fn run() -> io::Result<()> { msg::Request::ListMacros { dylib_path } => { msg::Response::ListMacros(srv.list_macros(&dylib_path)) } - msg::Request::ExpandMacro(task) => msg::Response::ExpandMacro(srv.expand(task)), + msg::Request::ExpandMacro(task) => match srv.span_mode() { + msg::SpanMode::Id => msg::Response::ExpandMacro(srv.expand(task).map(|(it, _)| it)), + msg::SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended( + srv.expand(task).map(|(tree, span_data_table)| msg::ExpandMacroExtended { + tree, + span_data_table, + }), + ), + }, msg::Request::ApiVersionCheck {} => { msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION) } + msg::Request::SetConfig(config) => { + srv.set_span_mode(config.span_mode); + msg::Response::SetConfig(config) + } }; write_response(res)? } diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml index 99993f16e276..b6686fa5b65d 100644 --- a/crates/proc-macro-srv/Cargo.toml +++ b/crates/proc-macro-srv/Cargo.toml @@ -26,13 +26,18 @@ stdx.workspace = true tt.workspace = true mbe.workspace = true paths.workspace = true +base-db.workspace = true +span.workspace = true proc-macro-api.workspace = true [dev-dependencies] expect-test = "1.4.0" # used as proc macro test targets -proc-macro-test.workspace = true +proc-macro-test.path = "./proc-macro-test" [features] -sysroot-abi = [] +sysroot-abi = ["proc-macro-test/sysroot-abi"] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/proc-macro-srv/proc-macro-test/Cargo.toml b/crates/proc-macro-srv/proc-macro-test/Cargo.toml new file mode 100644 index 000000000000..55be6bc23bbb --- /dev/null +++ b/crates/proc-macro-srv/proc-macro-test/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "proc-macro-test" +version = "0.0.0" +publish = false + +edition = "2021" +license = "MIT OR Apache-2.0" + +[lib] +doctest = false + +[build-dependencies] +cargo_metadata = "0.18.1" + +# local deps +toolchain = { path = "../../toolchain", version = "0.0.0" } + +[features] +sysroot-abi = [] diff --git a/crates/proc-macro-test/build.rs b/crates/proc-macro-srv/proc-macro-test/build.rs similarity index 97% rename from crates/proc-macro-test/build.rs rename to crates/proc-macro-srv/proc-macro-test/build.rs index 7827157865a9..7299147686df 100644 --- a/crates/proc-macro-test/build.rs +++ b/crates/proc-macro-srv/proc-macro-test/build.rs @@ -70,6 +70,9 @@ fn main() { // instance to use the same target directory. .arg("--target-dir") .arg(&target_dir); + if cfg!(feature = "sysroot-abi") { + cmd.args(["--features", "sysroot-abi"]); + } if let Ok(target) = std::env::var("TARGET") { cmd.args(["--target", &target]); diff --git a/crates/proc-macro-test/imp/.gitignore b/crates/proc-macro-srv/proc-macro-test/imp/.gitignore similarity index 100% rename from crates/proc-macro-test/imp/.gitignore rename to crates/proc-macro-srv/proc-macro-test/imp/.gitignore diff --git a/crates/proc-macro-test/imp/Cargo.toml b/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml similarity index 91% rename from crates/proc-macro-test/imp/Cargo.toml rename to crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml index 2a36737cef05..dc94fcd61a4f 100644 --- a/crates/proc-macro-test/imp/Cargo.toml +++ b/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml @@ -9,8 +9,11 @@ publish = false doctest = false proc-macro = true -[workspace] - [dependencies] # this crate should not have any dependencies, since it uses its own workspace, # and its own `Cargo.lock` + +[features] +sysroot-abi = [] + +[workspace] diff --git a/crates/proc-macro-test/imp/src/lib.rs b/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs similarity index 80% rename from crates/proc-macro-test/imp/src/lib.rs rename to crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs index 32510fba2f8c..03241b16be60 100644 --- a/crates/proc-macro-test/imp/src/lib.rs +++ b/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs @@ -1,6 +1,8 @@ //! Exports a few trivial procedural macros for testing. +#![cfg(any(feature = "sysroot-abi", rust_analyzer))] #![warn(rust_2018_idioms, unused_lifetimes)] +#![feature(proc_macro_span, proc_macro_def_site)] use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree}; @@ -49,6 +51,29 @@ pub fn fn_like_mk_idents(_args: TokenStream) -> TokenStream { TokenStream::from_iter(trees) } +#[proc_macro] +pub fn fn_like_span_join(args: TokenStream) -> TokenStream { + let args = &mut args.into_iter(); + let first = args.next().unwrap(); + let second = args.next().unwrap(); + TokenStream::from(TokenTree::from(Ident::new_raw( + "joined", + first.span().join(second.span()).unwrap(), + ))) +} + +#[proc_macro] +pub fn fn_like_span_ops(args: TokenStream) -> TokenStream { + let args = &mut args.into_iter(); + let mut first = args.next().unwrap(); + first.set_span(Span::def_site()); + let mut second = args.next().unwrap(); + second.set_span(second.span().resolved_at(Span::def_site())); + let mut third = args.next().unwrap(); + third.set_span(third.span().start()); + TokenStream::from_iter(vec![first, second, third]) +} + #[proc_macro_attribute] pub fn attr_noop(_args: TokenStream, item: TokenStream) -> TokenStream { item diff --git a/crates/proc-macro-test/src/lib.rs b/crates/proc-macro-srv/proc-macro-test/src/lib.rs similarity index 100% rename from crates/proc-macro-test/src/lib.rs rename to crates/proc-macro-srv/proc-macro-test/src/lib.rs diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs index f20e6832f6e9..52b4cced5f58 100644 --- a/crates/proc-macro-srv/src/dylib.rs +++ b/crates/proc-macro-srv/src/dylib.rs @@ -11,7 +11,10 @@ use libloading::Library; use memmap2::Mmap; use object::Object; use paths::AbsPath; -use proc_macro_api::{msg::TokenId, read_dylib_info, ProcMacroKind}; +use proc_macro::bridge; +use proc_macro_api::{read_dylib_info, ProcMacroKind}; + +use crate::ProcMacroSrvSpan; const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; @@ -147,15 +150,18 @@ impl Expander { Ok(Expander { inner: library }) } - pub fn expand( + pub fn expand( &self, macro_name: &str, - macro_body: &crate::tt::Subtree, - attributes: Option<&crate::tt::Subtree>, - def_site: TokenId, - call_site: TokenId, - mixed_site: TokenId, - ) -> Result { + macro_body: tt::Subtree, + attributes: Option>, + def_site: S, + call_site: S, + mixed_site: S, + ) -> Result, String> + where + ::TokenStream: Default, + { let result = self .inner .proc_macros diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index 56529f71d855..7cd6df2df86d 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs @@ -32,36 +32,67 @@ use std::{ }; use proc_macro_api::{ - msg::{self, ExpnGlobals, TokenId, CURRENT_API_VERSION}, + msg::{ + self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpnGlobals, + SpanMode, TokenId, CURRENT_API_VERSION, + }, ProcMacroKind, }; +use span::Span; -mod tt { - pub use proc_macro_api::msg::TokenId; - - pub use ::tt::*; - - pub type Subtree = ::tt::Subtree; - pub type TokenTree = ::tt::TokenTree; - pub type Delimiter = ::tt::Delimiter; - pub type Leaf = ::tt::Leaf; - pub type Literal = ::tt::Literal; - pub type Punct = ::tt::Punct; - pub type Ident = ::tt::Ident; -} +use crate::server::TokenStream; // see `build.rs` include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); +trait ProcMacroSrvSpan: tt::Span { + type Server: proc_macro::bridge::server::Server>; + fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server; +} + +impl ProcMacroSrvSpan for TokenId { + type Server = server::token_id::TokenIdServer; + + fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server { + Self::Server { interner: &server::SYMBOL_INTERNER, call_site, def_site, mixed_site } + } +} +impl ProcMacroSrvSpan for Span { + type Server = server::rust_analyzer_span::RaSpanServer; + fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server { + Self::Server { + interner: &server::SYMBOL_INTERNER, + call_site, + def_site, + mixed_site, + tracked_env_vars: Default::default(), + tracked_paths: Default::default(), + } + } +} + #[derive(Default)] pub struct ProcMacroSrv { expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>, + span_mode: SpanMode, } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; impl ProcMacroSrv { - pub fn expand(&mut self, task: msg::ExpandMacro) -> Result { + pub fn set_span_mode(&mut self, span_mode: SpanMode) { + self.span_mode = span_mode; + } + + pub fn span_mode(&self) -> SpanMode { + self.span_mode + } + + pub fn expand( + &mut self, + task: msg::ExpandMacro, + ) -> Result<(msg::FlatTree, Vec), msg::PanicMessage> { + let span_mode = self.span_mode; let expander = self.expander(task.lib.as_ref()).map_err(|err| { debug_assert!(false, "should list macros before asking to expand"); msg::PanicMessage(format!("failed to load macro: {err}")) @@ -71,10 +102,10 @@ impl ProcMacroSrv { for (k, v) in &task.env { env::set_var(k, v); } - let prev_working_dir = match task.current_dir { + let prev_working_dir = match &task.current_dir { Some(dir) => { let prev_working_dir = std::env::current_dir().ok(); - if let Err(err) = std::env::set_current_dir(&dir) { + if let Err(err) = std::env::set_current_dir(dir) { eprintln!("Failed to set the current working dir to {dir}. Error: {err:?}") } prev_working_dir @@ -83,38 +114,15 @@ impl ProcMacroSrv { }; let ExpnGlobals { def_site, call_site, mixed_site, .. } = task.has_global_spans; - let def_site = TokenId(def_site as u32); - let call_site = TokenId(call_site as u32); - let mixed_site = TokenId(mixed_site as u32); - let macro_body = task.macro_body.to_subtree_unresolved(CURRENT_API_VERSION); - let attributes = task.attributes.map(|it| it.to_subtree_unresolved(CURRENT_API_VERSION)); - let result = thread::scope(|s| { - let thread = thread::Builder::new() - .stack_size(EXPANDER_STACK_SIZE) - .name(task.macro_name.clone()) - .spawn_scoped(s, || { - expander - .expand( - &task.macro_name, - ¯o_body, - attributes.as_ref(), - def_site, - call_site, - mixed_site, - ) - .map(|it| msg::FlatTree::new_raw(&it, CURRENT_API_VERSION)) - }); - let res = match thread { - Ok(handle) => handle.join(), - Err(e) => std::panic::resume_unwind(Box::new(e)), - }; - - match res { - Ok(res) => res, - Err(e) => std::panic::resume_unwind(e), + let result = match span_mode { + SpanMode::Id => { + expand_id(task, expander, def_site, call_site, mixed_site).map(|it| (it, vec![])) } - }); + SpanMode::RustAnalyzer => { + expand_ra_span(task, expander, def_site, call_site, mixed_site) + } + }; prev_env.rollback(); @@ -155,6 +163,98 @@ impl ProcMacroSrv { } } +fn expand_id( + task: msg::ExpandMacro, + expander: &dylib::Expander, + def_site: usize, + call_site: usize, + mixed_site: usize, +) -> Result { + let def_site = TokenId(def_site as u32); + let call_site = TokenId(call_site as u32); + let mixed_site = TokenId(mixed_site as u32); + + let macro_body = task.macro_body.to_subtree_unresolved(CURRENT_API_VERSION); + let attributes = task.attributes.map(|it| it.to_subtree_unresolved(CURRENT_API_VERSION)); + let result = thread::scope(|s| { + let thread = thread::Builder::new() + .stack_size(EXPANDER_STACK_SIZE) + .name(task.macro_name.clone()) + .spawn_scoped(s, || { + expander + .expand( + &task.macro_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| msg::FlatTree::new_raw(&it, CURRENT_API_VERSION)) + }); + let res = match thread { + Ok(handle) => handle.join(), + Err(e) => std::panic::resume_unwind(Box::new(e)), + }; + + match res { + Ok(res) => res, + Err(e) => std::panic::resume_unwind(e), + } + }); + result +} + +fn expand_ra_span( + task: msg::ExpandMacro, + expander: &dylib::Expander, + def_site: usize, + call_site: usize, + mixed_site: usize, +) -> Result<(msg::FlatTree, Vec), String> { + let mut span_data_table = deserialize_span_data_index_map(&task.span_data_table); + + let def_site = span_data_table[def_site]; + let call_site = span_data_table[call_site]; + let mixed_site = span_data_table[mixed_site]; + + let macro_body = task.macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table); + let attributes = + task.attributes.map(|it| it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table)); + let result = thread::scope(|s| { + let thread = thread::Builder::new() + .stack_size(EXPANDER_STACK_SIZE) + .name(task.macro_name.clone()) + .spawn_scoped(s, || { + expander + .expand( + &task.macro_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| { + ( + msg::FlatTree::new(&it, CURRENT_API_VERSION, &mut span_data_table), + serialize_span_data_index_map(&span_data_table), + ) + }) + }); + let res = match thread { + Ok(handle) => handle.join(), + Err(e) => std::panic::resume_unwind(Box::new(e)), + }; + + match res { + Ok(res) => res, + Err(e) => std::panic::resume_unwind(e), + } + }); + result +} + pub struct PanicMessage { message: Option, } diff --git a/crates/proc-macro-srv/src/proc_macros.rs b/crates/proc-macro-srv/src/proc_macros.rs index 716b85d096d0..3fe968c81ca1 100644 --- a/crates/proc-macro-srv/src/proc_macros.rs +++ b/crates/proc-macro-srv/src/proc_macros.rs @@ -2,9 +2,9 @@ use libloading::Library; use proc_macro::bridge; -use proc_macro_api::{msg::TokenId, ProcMacroKind, RustCInfo}; +use proc_macro_api::{ProcMacroKind, RustCInfo}; -use crate::{dylib::LoadProcMacroDylibError, server::SYMBOL_INTERNER, tt}; +use crate::{dylib::LoadProcMacroDylibError, ProcMacroSrvSpan}; pub(crate) struct ProcMacros { exported_macros: Vec, @@ -40,19 +40,19 @@ impl ProcMacros { Err(LoadProcMacroDylibError::AbiMismatch(info.version_string)) } - pub(crate) fn expand( + pub(crate) fn expand( &self, macro_name: &str, - macro_body: &tt::Subtree, - attributes: Option<&tt::Subtree>, - def_site: TokenId, - call_site: TokenId, - mixed_site: TokenId, - ) -> Result { - let parsed_body = crate::server::TokenStream::with_subtree(macro_body.clone()); + macro_body: tt::Subtree, + attributes: Option>, + def_site: S, + call_site: S, + mixed_site: S, + ) -> Result, crate::PanicMessage> { + let parsed_body = crate::server::TokenStream::with_subtree(macro_body); - let parsed_attributes = attributes.map_or(crate::server::TokenStream::new(), |attr| { - crate::server::TokenStream::with_subtree(attr.clone()) + let parsed_attributes = attributes.map_or_else(crate::server::TokenStream::new, |attr| { + crate::server::TokenStream::with_subtree(attr) }); for proc_macro in &self.exported_macros { @@ -62,12 +62,7 @@ impl ProcMacros { { let res = client.run( &bridge::server::SameThread, - crate::server::RustAnalyzer { - interner: &SYMBOL_INTERNER, - call_site, - def_site, - mixed_site, - }, + S::make_server(call_site, def_site, mixed_site), parsed_body, false, ); @@ -78,12 +73,7 @@ impl ProcMacros { bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SameThread, - crate::server::RustAnalyzer { - interner: &SYMBOL_INTERNER, - call_site, - def_site, - mixed_site, - }, + S::make_server(call_site, def_site, mixed_site), parsed_body, false, ); @@ -94,13 +84,7 @@ impl ProcMacros { bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SameThread, - crate::server::RustAnalyzer { - interner: &SYMBOL_INTERNER, - - call_site, - def_site, - mixed_site, - }, + S::make_server(call_site, def_site, mixed_site), parsed_attributes, parsed_body, false, @@ -113,7 +97,7 @@ impl ProcMacros { } } - Err(bridge::PanicMessage::String("Nothing to expand".to_string()).into()) + Err(bridge::PanicMessage::String(format!("proc-macro `{macro_name}` is missing")).into()) } pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { diff --git a/crates/proc-macro-srv/src/server.rs b/crates/proc-macro-srv/src/server.rs index 917d8a6e26af..1854322ddb5c 100644 --- a/crates/proc-macro-srv/src/server.rs +++ b/crates/proc-macro-srv/src/server.rs @@ -8,226 +8,18 @@ //! //! FIXME: No span and source file information is implemented yet -use proc_macro::bridge::{self, server}; +use proc_macro::bridge; mod token_stream; -use proc_macro_api::msg::TokenId; pub use token_stream::TokenStream; -use token_stream::TokenStreamBuilder; +pub mod token_id; +pub mod rust_analyzer_span; mod symbol; pub use symbol::*; +use tt::Spacing; -use std::{ - iter, - ops::{Bound, Range}, -}; - -use crate::tt; - -type Group = tt::Subtree; -type TokenTree = tt::TokenTree; -#[allow(unused)] -type Punct = tt::Punct; -type Spacing = tt::Spacing; -#[allow(unused)] -type Literal = tt::Literal; -type Span = tt::TokenId; - -#[derive(Clone)] -pub struct SourceFile { - // FIXME stub -} - -pub struct FreeFunctions; - -pub struct RustAnalyzer { - // FIXME: store span information here. - pub(crate) interner: SymbolInternerRef, - pub call_site: TokenId, - pub def_site: TokenId, - pub mixed_site: TokenId, -} - -impl server::Types for RustAnalyzer { - type FreeFunctions = FreeFunctions; - type TokenStream = TokenStream; - type SourceFile = SourceFile; - type Span = Span; - type Symbol = Symbol; -} - -impl server::FreeFunctions for RustAnalyzer { - fn injected_env_var(&mut self, _var: &str) -> Option { - None - } - - fn track_env_var(&mut self, _var: &str, _value: Option<&str>) { - // FIXME: track env var accesses - // https://github.com/rust-lang/rust/pull/71858 - } - fn track_path(&mut self, _path: &str) {} - - fn literal_from_str( - &mut self, - s: &str, - ) -> Result, ()> { - // FIXME: keep track of LitKind and Suffix - Ok(bridge::Literal { - kind: bridge::LitKind::Err, - symbol: Symbol::intern(self.interner, s), - suffix: None, - span: self.call_site, - }) - } - - fn emit_diagnostic(&mut self, _: bridge::Diagnostic) { - // FIXME handle diagnostic - } -} - -impl server::TokenStream for RustAnalyzer { - fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { - stream.is_empty() - } - fn from_str(&mut self, src: &str) -> Self::TokenStream { - Self::TokenStream::from_str(src, self.call_site).expect("cannot parse string") - } - fn to_string(&mut self, stream: &Self::TokenStream) -> String { - stream.to_string() - } - fn from_token_tree( - &mut self, - tree: bridge::TokenTree, - ) -> Self::TokenStream { - match tree { - bridge::TokenTree::Group(group) => { - let group = Group { - delimiter: delim_to_internal(group.delimiter, group.span), - token_trees: match group.stream { - Some(stream) => stream.into_iter().collect(), - None => Vec::new(), - }, - }; - let tree = TokenTree::from(group); - Self::TokenStream::from_iter(iter::once(tree)) - } - - bridge::TokenTree::Ident(ident) => { - let text = ident.sym.text(self.interner); - let text = - if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text }; - let ident: tt::Ident = tt::Ident { text, span: ident.span }; - let leaf = tt::Leaf::from(ident); - let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) - } - - bridge::TokenTree::Literal(literal) => { - let literal = LiteralFormatter(literal); - let text = literal.with_stringify_parts(self.interner, |parts| { - ::tt::SmolStr::from_iter(parts.iter().copied()) - }); - - let literal = tt::Literal { text, span: literal.0.span }; - let leaf = tt::Leaf::from(literal); - let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) - } - - bridge::TokenTree::Punct(p) => { - let punct = tt::Punct { - char: p.ch as char, - spacing: if p.joint { Spacing::Joint } else { Spacing::Alone }, - span: p.span, - }; - let leaf = tt::Leaf::from(punct); - let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) - } - } - } - - fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result { - Ok(self_.clone()) - } - - fn concat_trees( - &mut self, - base: Option, - trees: Vec>, - ) -> Self::TokenStream { - let mut builder = TokenStreamBuilder::new(); - if let Some(base) = base { - builder.push(base); - } - for tree in trees { - builder.push(self.from_token_tree(tree)); - } - builder.build() - } - - fn concat_streams( - &mut self, - base: Option, - streams: Vec, - ) -> Self::TokenStream { - let mut builder = TokenStreamBuilder::new(); - if let Some(base) = base { - builder.push(base); - } - for stream in streams { - builder.push(stream); - } - builder.build() - } - - fn into_trees( - &mut self, - stream: Self::TokenStream, - ) -> Vec> { - stream - .into_iter() - .map(|tree| match tree { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { - bridge::TokenTree::Ident(bridge::Ident { - sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")), - is_raw: ident.text.starts_with("r#"), - span: ident.span, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - bridge::TokenTree::Literal(bridge::Literal { - // FIXME: handle literal kinds - kind: bridge::LitKind::Err, - symbol: Symbol::intern(self.interner, &lit.text), - // FIXME: handle suffixes - suffix: None, - span: lit.span, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { - bridge::TokenTree::Punct(bridge::Punct { - ch: punct.char as u8, - joint: punct.spacing == Spacing::Joint, - span: punct.span, - }) - } - tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group { - delimiter: delim_to_external(subtree.delimiter), - stream: if subtree.token_trees.is_empty() { - None - } else { - Some(subtree.token_trees.into_iter().collect()) - }, - span: bridge::DelimSpan::from_single(subtree.delimiter.open), - }), - }) - .collect() - } -} - -fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan) -> tt::Delimiter { +fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan) -> tt::Delimiter { let kind = match d { proc_macro::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis, proc_macro::Delimiter::Brace => tt::DelimiterKind::Brace, @@ -237,7 +29,7 @@ fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan) -> tt::Delimiter { open: span.open, close: span.close, kind } } -fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter { +fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter { match d.kind { tt::DelimiterKind::Parenthesis => proc_macro::Delimiter::Parenthesis, tt::DelimiterKind::Brace => proc_macro::Delimiter::Brace, @@ -262,121 +54,9 @@ fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing { } } -impl server::SourceFile for RustAnalyzer { - // FIXME these are all stubs - fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool { - true - } - fn path(&mut self, _file: &Self::SourceFile) -> String { - String::new() - } - fn is_real(&mut self, _file: &Self::SourceFile) -> bool { - true - } -} +struct LiteralFormatter(bridge::Literal); -impl server::Span for RustAnalyzer { - fn debug(&mut self, span: Self::Span) -> String { - format!("{:?}", span.0) - } - fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile { - SourceFile {} - } - fn save_span(&mut self, _span: Self::Span) -> usize { - // FIXME stub - 0 - } - fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span { - // FIXME stub - self.call_site - } - /// Recent feature, not yet in the proc_macro - /// - /// See PR: - /// https://github.com/rust-lang/rust/pull/55780 - fn source_text(&mut self, _span: Self::Span) -> Option { - None - } - - fn parent(&mut self, _span: Self::Span) -> Option { - // FIXME handle span - None - } - fn source(&mut self, span: Self::Span) -> Self::Span { - // FIXME handle span - span - } - fn byte_range(&mut self, _span: Self::Span) -> Range { - // FIXME handle span - Range { start: 0, end: 0 } - } - fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option { - // Just return the first span again, because some macros will unwrap the result. - Some(first) - } - fn subspan( - &mut self, - span: Self::Span, - _start: Bound, - _end: Bound, - ) -> Option { - // Just return the span again, because some macros will unwrap the result. - Some(span) - } - fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span { - // FIXME handle span - self.call_site - } - - fn end(&mut self, _self_: Self::Span) -> Self::Span { - self.call_site - } - - fn start(&mut self, _self_: Self::Span) -> Self::Span { - self.call_site - } - - fn line(&mut self, _span: Self::Span) -> usize { - // FIXME handle line - 0 - } - - fn column(&mut self, _span: Self::Span) -> usize { - // FIXME handle column - 0 - } -} - -impl server::Symbol for RustAnalyzer { - fn normalize_and_validate_ident(&mut self, string: &str) -> Result { - // FIXME: nfc-normalize and validate idents - Ok(::intern_symbol(string)) - } -} - -impl server::Server for RustAnalyzer { - fn globals(&mut self) -> bridge::ExpnGlobals { - bridge::ExpnGlobals { - def_site: self.def_site, - call_site: self.call_site, - mixed_site: self.mixed_site, - } - } - - fn intern_symbol(ident: &str) -> Self::Symbol { - // FIXME: should be `self.interner` once the proc-macro api allows it. - Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident)) - } - - fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - // FIXME: should be `self.interner` once the proc-macro api allows it. - f(symbol.text(&SYMBOL_INTERNER).as_str()) - } -} - -struct LiteralFormatter(bridge::Literal); - -impl LiteralFormatter { +impl LiteralFormatter { /// Invokes the callback with a `&[&str]` consisting of each part of the /// literal's representation. This is done to allow the `ToString` and /// `Display` implementations to borrow references to symbol values, and @@ -427,66 +107,3 @@ impl LiteralFormatter { f(symbol.as_str(), suffix.as_str()) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_ra_server_to_string() { - let s = TokenStream { - token_trees: vec![ - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "struct".into(), - span: tt::TokenId(0), - })), - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "T".into(), - span: tt::TokenId(0), - })), - tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: tt::TokenId(0), - close: tt::TokenId(0), - kind: tt::DelimiterKind::Brace, - }, - token_trees: vec![], - }), - ], - }; - - assert_eq!(s.to_string(), "struct T {}"); - } - - #[test] - fn test_ra_server_from_str() { - let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: tt::TokenId(0), - close: tt::TokenId(0), - kind: tt::DelimiterKind::Parenthesis, - }, - token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "a".into(), - span: tt::TokenId(0), - }))], - }); - - let t1 = TokenStream::from_str("(a)", tt::TokenId(0)).unwrap(); - assert_eq!(t1.token_trees.len(), 1); - assert_eq!(t1.token_trees[0], subtree_paren_a); - - let t2 = TokenStream::from_str("(a);", tt::TokenId(0)).unwrap(); - assert_eq!(t2.token_trees.len(), 2); - assert_eq!(t2.token_trees[0], subtree_paren_a); - - let underscore = TokenStream::from_str("_", tt::TokenId(0)).unwrap(); - assert_eq!( - underscore.token_trees[0], - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "_".into(), - span: tt::TokenId(0), - })) - ); - } -} diff --git a/crates/proc-macro-srv/src/server/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server/rust_analyzer_span.rs new file mode 100644 index 000000000000..bcf3600d2736 --- /dev/null +++ b/crates/proc-macro-srv/src/server/rust_analyzer_span.rs @@ -0,0 +1,411 @@ +//! proc-macro server backend based on rust-analyzer's internal span represention +//! This backend is used solely by rust-analyzer as it ties into rust-analyzer internals. +//! +//! It is an unfortunate result of how the proc-macro API works that we need to look into the +//! concrete representation of the spans, and as such, RustRover cannot make use of this unless they +//! change their representation to be compatible with rust-analyzer's. +use std::{ + collections::{HashMap, HashSet}, + iter, + ops::{Bound, Range}, +}; + +use ::tt::{TextRange, TextSize}; +use proc_macro::bridge::{self, server}; +use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER}; + +use crate::server::{ + delim_to_external, delim_to_internal, token_stream::TokenStreamBuilder, LiteralFormatter, + Symbol, SymbolInternerRef, SYMBOL_INTERNER, +}; +mod tt { + pub use ::tt::*; + + pub type Subtree = ::tt::Subtree; + pub type TokenTree = ::tt::TokenTree; + pub type Leaf = ::tt::Leaf; + pub type Literal = ::tt::Literal; + pub type Punct = ::tt::Punct; + pub type Ident = ::tt::Ident; +} + +type TokenStream = crate::server::TokenStream; + +#[derive(Clone)] +pub struct SourceFile; +pub struct FreeFunctions; + +pub struct RaSpanServer { + pub(crate) interner: SymbolInternerRef, + // FIXME: Report this back to the caller to track as dependencies + pub tracked_env_vars: HashMap, Option>>, + // FIXME: Report this back to the caller to track as dependencies + pub tracked_paths: HashSet>, + pub call_site: Span, + pub def_site: Span, + pub mixed_site: Span, +} + +impl server::Types for RaSpanServer { + type FreeFunctions = FreeFunctions; + type TokenStream = TokenStream; + type SourceFile = SourceFile; + type Span = Span; + type Symbol = Symbol; +} + +impl server::FreeFunctions for RaSpanServer { + fn injected_env_var(&mut self, _: &str) -> Option { + None + } + + fn track_env_var(&mut self, var: &str, value: Option<&str>) { + self.tracked_env_vars.insert(var.into(), value.map(Into::into)); + } + fn track_path(&mut self, path: &str) { + self.tracked_paths.insert(path.into()); + } + + fn literal_from_str( + &mut self, + s: &str, + ) -> Result, ()> { + // FIXME: keep track of LitKind and Suffix + Ok(bridge::Literal { + kind: bridge::LitKind::Err, + symbol: Symbol::intern(self.interner, s), + suffix: None, + span: self.call_site, + }) + } + + fn emit_diagnostic(&mut self, _: bridge::Diagnostic) { + // FIXME handle diagnostic + } +} + +impl server::TokenStream for RaSpanServer { + fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { + stream.is_empty() + } + fn from_str(&mut self, src: &str) -> Self::TokenStream { + Self::TokenStream::from_str(src, self.call_site).expect("cannot parse string") + } + fn to_string(&mut self, stream: &Self::TokenStream) -> String { + stream.to_string() + } + fn from_token_tree( + &mut self, + tree: bridge::TokenTree, + ) -> Self::TokenStream { + match tree { + bridge::TokenTree::Group(group) => { + let group = tt::Subtree { + delimiter: delim_to_internal(group.delimiter, group.span), + token_trees: match group.stream { + Some(stream) => stream.into_iter().collect(), + None => Vec::new(), + }, + }; + let tree = tt::TokenTree::from(group); + Self::TokenStream::from_iter(iter::once(tree)) + } + + bridge::TokenTree::Ident(ident) => { + let text = ident.sym.text(self.interner); + let text = + if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text }; + let ident: tt::Ident = tt::Ident { text, span: ident.span }; + let leaf = tt::Leaf::from(ident); + let tree = tt::TokenTree::from(leaf); + Self::TokenStream::from_iter(iter::once(tree)) + } + + bridge::TokenTree::Literal(literal) => { + let literal = LiteralFormatter(literal); + let text = literal.with_stringify_parts(self.interner, |parts| { + ::tt::SmolStr::from_iter(parts.iter().copied()) + }); + + let literal = tt::Literal { text, span: literal.0.span }; + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + Self::TokenStream::from_iter(iter::once(tree)) + } + + bridge::TokenTree::Punct(p) => { + let punct = tt::Punct { + char: p.ch as char, + spacing: if p.joint { tt::Spacing::Joint } else { tt::Spacing::Alone }, + span: p.span, + }; + let leaf = tt::Leaf::from(punct); + let tree = tt::TokenTree::from(leaf); + Self::TokenStream::from_iter(iter::once(tree)) + } + } + } + + fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result { + // FIXME: requires db, more importantly this requires name resolution so we would need to + // eagerly expand this proc-macro, but we can't know that this proc-macro is eager until we + // expand it ... + // This calls for some kind of marker that a proc-macro wants to access this eager API, + // otherwise we need to treat every proc-macro eagerly / or not support this. + Ok(self_.clone()) + } + + fn concat_trees( + &mut self, + base: Option, + trees: Vec>, + ) -> Self::TokenStream { + let mut builder = TokenStreamBuilder::new(); + if let Some(base) = base { + builder.push(base); + } + for tree in trees { + builder.push(self.from_token_tree(tree)); + } + builder.build() + } + + fn concat_streams( + &mut self, + base: Option, + streams: Vec, + ) -> Self::TokenStream { + let mut builder = TokenStreamBuilder::new(); + if let Some(base) = base { + builder.push(base); + } + for stream in streams { + builder.push(stream); + } + builder.build() + } + + fn into_trees( + &mut self, + stream: Self::TokenStream, + ) -> Vec> { + stream + .into_iter() + .map(|tree| match tree { + tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { + bridge::TokenTree::Ident(bridge::Ident { + sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")), + is_raw: ident.text.starts_with("r#"), + span: ident.span, + }) + } + tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { + bridge::TokenTree::Literal(bridge::Literal { + // FIXME: handle literal kinds + kind: bridge::LitKind::Err, + symbol: Symbol::intern(self.interner, &lit.text), + // FIXME: handle suffixes + suffix: None, + span: lit.span, + }) + } + tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { + bridge::TokenTree::Punct(bridge::Punct { + ch: punct.char as u8, + joint: punct.spacing == tt::Spacing::Joint, + span: punct.span, + }) + } + tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group { + delimiter: delim_to_external(subtree.delimiter), + stream: if subtree.token_trees.is_empty() { + None + } else { + Some(subtree.token_trees.into_iter().collect()) + }, + span: bridge::DelimSpan::from_single(subtree.delimiter.open), + }), + }) + .collect() + } +} + +impl server::SourceFile for RaSpanServer { + // FIXME these are all stubs + fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool { + true + } + fn path(&mut self, _file: &Self::SourceFile) -> String { + String::new() + } + fn is_real(&mut self, _file: &Self::SourceFile) -> bool { + true + } +} + +impl server::Span for RaSpanServer { + fn debug(&mut self, span: Self::Span) -> String { + format!("{:?}", span) + } + fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile { + // FIXME stub, requires db + SourceFile {} + } + fn save_span(&mut self, _span: Self::Span) -> usize { + // FIXME stub, requires builtin quote! implementation + 0 + } + fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span { + // FIXME stub, requires builtin quote! implementation + self.call_site + } + /// Recent feature, not yet in the proc_macro + /// + /// See PR: + /// https://github.com/rust-lang/rust/pull/55780 + fn source_text(&mut self, _span: Self::Span) -> Option { + // FIXME requires db, needs special handling wrt fixup spans + None + } + + fn parent(&mut self, _span: Self::Span) -> Option { + // FIXME requires db, looks up the parent call site + None + } + fn source(&mut self, span: Self::Span) -> Self::Span { + // FIXME requires db, returns the top level call site + span + } + fn byte_range(&mut self, span: Self::Span) -> Range { + // FIXME requires db to resolve the ast id, THIS IS NOT INCREMENTAL + Range { start: span.range.start().into(), end: span.range.end().into() } + } + fn join(&mut self, first: Self::Span, second: Self::Span) -> Option { + // We can't modify the span range for fixup spans, those are meaningful to fixup, so just + // prefer the non-fixup span. + if first.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + return Some(second); + } + if second.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + return Some(first); + } + // FIXME: Once we can talk back to the client, implement a "long join" request for anchors + // that differ in [AstId]s as joining those spans requires resolving the AstIds. + if first.anchor != second.anchor { + return None; + } + // Differing context, we can't merge these so prefer the one that's root + if first.ctx != second.ctx { + if first.ctx.is_root() { + return Some(second); + } else if second.ctx.is_root() { + return Some(first); + } + } + Some(Span { + range: first.range.cover(second.range), + anchor: second.anchor, + ctx: second.ctx, + }) + } + fn subspan( + &mut self, + span: Self::Span, + start: Bound, + end: Bound, + ) -> Option { + // We can't modify the span range for fixup spans, those are meaningful to fixup. + if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + return Some(span); + } + let length = span.range.len().into(); + + let start: u32 = match start { + Bound::Included(lo) => lo, + Bound::Excluded(lo) => lo.checked_add(1)?, + Bound::Unbounded => 0, + } + .try_into() + .ok()?; + + let end: u32 = match end { + Bound::Included(hi) => hi.checked_add(1)?, + Bound::Excluded(hi) => hi, + Bound::Unbounded => span.range.len().into(), + } + .try_into() + .ok()?; + + // Bounds check the values, preventing addition overflow and OOB spans. + let span_start = span.range.start().into(); + if (u32::MAX - start) < span_start + || (u32::MAX - end) < span_start + || start >= end + || end > length + { + return None; + } + + Some(Span { + range: TextRange::new(TextSize::from(start), TextSize::from(end)) + span.range.start(), + ..span + }) + } + + fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { + Span { ctx: at.ctx, ..span } + } + + fn end(&mut self, span: Self::Span) -> Self::Span { + // We can't modify the span range for fixup spans, those are meaningful to fixup. + if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + return span; + } + Span { range: TextRange::empty(span.range.end()), ..span } + } + + fn start(&mut self, span: Self::Span) -> Self::Span { + // We can't modify the span range for fixup spans, those are meaningful to fixup. + if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + return span; + } + Span { range: TextRange::empty(span.range.start()), ..span } + } + + fn line(&mut self, _span: Self::Span) -> usize { + // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL + 0 + } + + fn column(&mut self, _span: Self::Span) -> usize { + // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL + 0 + } +} + +impl server::Symbol for RaSpanServer { + fn normalize_and_validate_ident(&mut self, string: &str) -> Result { + // FIXME: nfc-normalize and validate idents + Ok(::intern_symbol(string)) + } +} + +impl server::Server for RaSpanServer { + fn globals(&mut self) -> bridge::ExpnGlobals { + bridge::ExpnGlobals { + def_site: self.def_site, + call_site: self.call_site, + mixed_site: self.mixed_site, + } + } + + fn intern_symbol(ident: &str) -> Self::Symbol { + // FIXME: should be `self.interner` once the proc-macro api allows it. + Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident)) + } + + fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { + // FIXME: should be `self.interner` once the proc-macro api allows it. + f(symbol.text(&SYMBOL_INTERNER).as_str()) + } +} diff --git a/crates/proc-macro-srv/src/server/token_id.rs b/crates/proc-macro-srv/src/server/token_id.rs new file mode 100644 index 000000000000..12526ad4f3ae --- /dev/null +++ b/crates/proc-macro-srv/src/server/token_id.rs @@ -0,0 +1,380 @@ +//! proc-macro server backend based on [`proc_macro_api::msg::TokenId`] as the backing span. +//! This backend is rather inflexible, used by RustRover and older rust-analyzer versions. +use std::{ + iter, + ops::{Bound, Range}, +}; + +use proc_macro::bridge::{self, server}; + +use crate::server::{ + delim_to_external, delim_to_internal, token_stream::TokenStreamBuilder, LiteralFormatter, + Symbol, SymbolInternerRef, SYMBOL_INTERNER, +}; +mod tt { + pub use proc_macro_api::msg::TokenId; + + pub use ::tt::*; + + pub type Subtree = ::tt::Subtree; + pub type TokenTree = ::tt::TokenTree; + pub type Leaf = ::tt::Leaf; + pub type Literal = ::tt::Literal; + pub type Punct = ::tt::Punct; + pub type Ident = ::tt::Ident; +} +type Group = tt::Subtree; +type TokenTree = tt::TokenTree; +#[allow(unused)] +type Punct = tt::Punct; +type Spacing = tt::Spacing; +#[allow(unused)] +type Literal = tt::Literal; +type Span = tt::TokenId; +type TokenStream = crate::server::TokenStream; + +#[derive(Clone)] +pub struct SourceFile; +pub struct FreeFunctions; + +pub struct TokenIdServer { + pub(crate) interner: SymbolInternerRef, + pub call_site: Span, + pub def_site: Span, + pub mixed_site: Span, +} + +impl server::Types for TokenIdServer { + type FreeFunctions = FreeFunctions; + type TokenStream = TokenStream; + type SourceFile = SourceFile; + type Span = Span; + type Symbol = Symbol; +} + +impl server::FreeFunctions for TokenIdServer { + fn injected_env_var(&mut self, _: &str) -> Option { + None + } + fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {} + fn track_path(&mut self, _path: &str) {} + fn literal_from_str( + &mut self, + s: &str, + ) -> Result, ()> { + // FIXME: keep track of LitKind and Suffix + Ok(bridge::Literal { + kind: bridge::LitKind::Err, + symbol: Symbol::intern(self.interner, s), + suffix: None, + span: self.call_site, + }) + } + + fn emit_diagnostic(&mut self, _: bridge::Diagnostic) {} +} + +impl server::TokenStream for TokenIdServer { + fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { + stream.is_empty() + } + fn from_str(&mut self, src: &str) -> Self::TokenStream { + Self::TokenStream::from_str(src, self.call_site).expect("cannot parse string") + } + fn to_string(&mut self, stream: &Self::TokenStream) -> String { + stream.to_string() + } + fn from_token_tree( + &mut self, + tree: bridge::TokenTree, + ) -> Self::TokenStream { + match tree { + bridge::TokenTree::Group(group) => { + let group = Group { + delimiter: delim_to_internal(group.delimiter, group.span), + token_trees: match group.stream { + Some(stream) => stream.into_iter().collect(), + None => Vec::new(), + }, + }; + let tree = TokenTree::from(group); + Self::TokenStream::from_iter(iter::once(tree)) + } + + bridge::TokenTree::Ident(ident) => { + let text = ident.sym.text(self.interner); + let text = + if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text }; + let ident: tt::Ident = tt::Ident { text, span: ident.span }; + let leaf = tt::Leaf::from(ident); + let tree = TokenTree::from(leaf); + Self::TokenStream::from_iter(iter::once(tree)) + } + + bridge::TokenTree::Literal(literal) => { + let literal = LiteralFormatter(literal); + let text = literal.with_stringify_parts(self.interner, |parts| { + ::tt::SmolStr::from_iter(parts.iter().copied()) + }); + + let literal = tt::Literal { text, span: literal.0.span }; + let leaf = tt::Leaf::from(literal); + let tree = TokenTree::from(leaf); + Self::TokenStream::from_iter(iter::once(tree)) + } + + bridge::TokenTree::Punct(p) => { + let punct = tt::Punct { + char: p.ch as char, + spacing: if p.joint { Spacing::Joint } else { Spacing::Alone }, + span: p.span, + }; + let leaf = tt::Leaf::from(punct); + let tree = TokenTree::from(leaf); + Self::TokenStream::from_iter(iter::once(tree)) + } + } + } + + fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result { + Ok(self_.clone()) + } + + fn concat_trees( + &mut self, + base: Option, + trees: Vec>, + ) -> Self::TokenStream { + let mut builder = TokenStreamBuilder::new(); + if let Some(base) = base { + builder.push(base); + } + for tree in trees { + builder.push(self.from_token_tree(tree)); + } + builder.build() + } + + fn concat_streams( + &mut self, + base: Option, + streams: Vec, + ) -> Self::TokenStream { + let mut builder = TokenStreamBuilder::new(); + if let Some(base) = base { + builder.push(base); + } + for stream in streams { + builder.push(stream); + } + builder.build() + } + + fn into_trees( + &mut self, + stream: Self::TokenStream, + ) -> Vec> { + stream + .into_iter() + .map(|tree| match tree { + tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { + bridge::TokenTree::Ident(bridge::Ident { + sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")), + is_raw: ident.text.starts_with("r#"), + span: ident.span, + }) + } + tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { + bridge::TokenTree::Literal(bridge::Literal { + // FIXME: handle literal kinds + kind: bridge::LitKind::Err, + symbol: Symbol::intern(self.interner, &lit.text), + // FIXME: handle suffixes + suffix: None, + span: lit.span, + }) + } + tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { + bridge::TokenTree::Punct(bridge::Punct { + ch: punct.char as u8, + joint: punct.spacing == Spacing::Joint, + span: punct.span, + }) + } + tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group { + delimiter: delim_to_external(subtree.delimiter), + stream: if subtree.token_trees.is_empty() { + None + } else { + Some(subtree.token_trees.into_iter().collect()) + }, + span: bridge::DelimSpan::from_single(subtree.delimiter.open), + }), + }) + .collect() + } +} + +impl server::SourceFile for TokenIdServer { + fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool { + true + } + fn path(&mut self, _file: &Self::SourceFile) -> String { + String::new() + } + fn is_real(&mut self, _file: &Self::SourceFile) -> bool { + true + } +} + +impl server::Span for TokenIdServer { + fn debug(&mut self, span: Self::Span) -> String { + format!("{:?}", span.0) + } + fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile { + SourceFile {} + } + fn save_span(&mut self, _span: Self::Span) -> usize { + 0 + } + fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span { + self.call_site + } + /// Recent feature, not yet in the proc_macro + /// + /// See PR: + /// https://github.com/rust-lang/rust/pull/55780 + fn source_text(&mut self, _span: Self::Span) -> Option { + None + } + + fn parent(&mut self, _span: Self::Span) -> Option { + None + } + fn source(&mut self, span: Self::Span) -> Self::Span { + span + } + fn byte_range(&mut self, _span: Self::Span) -> Range { + Range { start: 0, end: 0 } + } + fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option { + // Just return the first span again, because some macros will unwrap the result. + Some(first) + } + fn subspan( + &mut self, + span: Self::Span, + _start: Bound, + _end: Bound, + ) -> Option { + // Just return the span again, because some macros will unwrap the result. + Some(span) + } + fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span { + self.call_site + } + + fn end(&mut self, _self_: Self::Span) -> Self::Span { + self.call_site + } + + fn start(&mut self, _self_: Self::Span) -> Self::Span { + self.call_site + } + + fn line(&mut self, _span: Self::Span) -> usize { + 0 + } + + fn column(&mut self, _span: Self::Span) -> usize { + 0 + } +} + +impl server::Symbol for TokenIdServer { + fn normalize_and_validate_ident(&mut self, string: &str) -> Result { + // FIXME: nfc-normalize and validate idents + Ok(::intern_symbol(string)) + } +} + +impl server::Server for TokenIdServer { + fn globals(&mut self) -> bridge::ExpnGlobals { + bridge::ExpnGlobals { + def_site: self.def_site, + call_site: self.call_site, + mixed_site: self.mixed_site, + } + } + + fn intern_symbol(ident: &str) -> Self::Symbol { + Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident)) + } + + fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { + f(symbol.text(&SYMBOL_INTERNER).as_str()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ra_server_to_string() { + let s = TokenStream { + token_trees: vec![ + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: "struct".into(), + span: tt::TokenId(0), + })), + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: "T".into(), + span: tt::TokenId(0), + })), + tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: tt::TokenId(0), + close: tt::TokenId(0), + kind: tt::DelimiterKind::Brace, + }, + token_trees: vec![], + }), + ], + }; + + assert_eq!(s.to_string(), "struct T {}"); + } + + #[test] + fn test_ra_server_from_str() { + let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: tt::TokenId(0), + close: tt::TokenId(0), + kind: tt::DelimiterKind::Parenthesis, + }, + token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: "a".into(), + span: tt::TokenId(0), + }))], + }); + + let t1 = TokenStream::from_str("(a)", tt::TokenId(0)).unwrap(); + assert_eq!(t1.token_trees.len(), 1); + assert_eq!(t1.token_trees[0], subtree_paren_a); + + let t2 = TokenStream::from_str("(a);", tt::TokenId(0)).unwrap(); + assert_eq!(t2.token_trees.len(), 2); + assert_eq!(t2.token_trees[0], subtree_paren_a); + + let underscore = TokenStream::from_str("_", tt::TokenId(0)).unwrap(); + assert_eq!( + underscore.token_trees[0], + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: "_".into(), + span: tt::TokenId(0), + })) + ); + } +} diff --git a/crates/proc-macro-srv/src/server/token_stream.rs b/crates/proc-macro-srv/src/server/token_stream.rs index 36be88250388..8f669a30494b 100644 --- a/crates/proc-macro-srv/src/server/token_stream.rs +++ b/crates/proc-macro-srv/src/server/token_stream.rs @@ -1,20 +1,24 @@ //! TokenStream implementation used by sysroot ABI -use proc_macro_api::msg::TokenId; +use tt::TokenTree; -use crate::tt::{self, TokenTree}; - -#[derive(Debug, Default, Clone)] -pub struct TokenStream { - pub(super) token_trees: Vec, +#[derive(Debug, Clone)] +pub struct TokenStream { + pub(super) token_trees: Vec>, } -impl TokenStream { +impl Default for TokenStream { + fn default() -> Self { + Self { token_trees: vec![] } + } +} + +impl TokenStream { pub(crate) fn new() -> Self { - TokenStream::default() + TokenStream { token_trees: vec![] } } - pub(crate) fn with_subtree(subtree: tt::Subtree) -> Self { + pub(crate) fn with_subtree(subtree: tt::Subtree) -> Self { if subtree.delimiter.kind != tt::DelimiterKind::Invisible { TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } } else { @@ -22,7 +26,10 @@ impl TokenStream { } } - pub(crate) fn into_subtree(self, call_site: TokenId) -> tt::Subtree { + pub(crate) fn into_subtree(self, call_site: S) -> tt::Subtree + where + S: Copy, + { tt::Subtree { delimiter: tt::Delimiter { open: call_site, @@ -39,37 +46,37 @@ impl TokenStream { } /// Creates a token stream containing a single token tree. -impl From for TokenStream { - fn from(tree: TokenTree) -> TokenStream { +impl From> for TokenStream { + fn from(tree: TokenTree) -> TokenStream { TokenStream { token_trees: vec![tree] } } } /// Collects a number of token trees into a single stream. -impl FromIterator for TokenStream { - fn from_iter>(trees: I) -> Self { +impl FromIterator> for TokenStream { + fn from_iter>>(trees: I) -> Self { trees.into_iter().map(TokenStream::from).collect() } } /// A "flattening" operation on token streams, collects token trees /// from multiple token streams into a single stream. -impl FromIterator for TokenStream { - fn from_iter>(streams: I) -> Self { +impl FromIterator> for TokenStream { + fn from_iter>>(streams: I) -> Self { let mut builder = TokenStreamBuilder::new(); streams.into_iter().for_each(|stream| builder.push(stream)); builder.build() } } -impl Extend for TokenStream { - fn extend>(&mut self, trees: I) { +impl Extend> for TokenStream { + fn extend>>(&mut self, trees: I) { self.extend(trees.into_iter().map(TokenStream::from)); } } -impl Extend for TokenStream { - fn extend>(&mut self, streams: I) { +impl Extend> for TokenStream { + fn extend>>(&mut self, streams: I) { for item in streams { for tkn in item { match tkn { @@ -87,22 +94,21 @@ impl Extend for TokenStream { } } -pub(super) struct TokenStreamBuilder { - acc: TokenStream, +pub(super) struct TokenStreamBuilder { + acc: TokenStream, } /// pub(super)lic implementation details for the `TokenStream` type, such as iterators. pub(super) mod token_stream { - use proc_macro_api::msg::TokenId; - use super::{tt, TokenStream, TokenTree}; + use super::{TokenStream, TokenTree}; /// An iterator over `TokenStream`'s `TokenTree`s. /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, /// and returns whole groups as token trees. - impl IntoIterator for TokenStream { - type Item = TokenTree; - type IntoIter = std::vec::IntoIter; + impl IntoIterator for TokenStream { + type Item = TokenTree; + type IntoIter = std::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.token_trees.into_iter() @@ -119,71 +125,34 @@ pub(super) mod token_stream { /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to /// change these errors into `LexError`s later. #[rustfmt::skip] - impl /*FromStr for*/ TokenStream { + impl /*FromStr for*/ TokenStream { // type Err = LexError; - pub(crate) fn from_str(src: &str, call_site: TokenId) -> Result { + pub(crate) fn from_str(src: &str, call_site: S) -> Result, LexError> { let subtree = mbe::parse_to_token_tree_static_span(call_site, src).ok_or("Failed to parse from mbe")?; - let subtree = subtree_replace_token_ids_with_call_site(subtree,call_site); Ok(TokenStream::with_subtree(subtree)) } } - impl ToString for TokenStream { + impl ToString for TokenStream { fn to_string(&self) -> String { ::tt::pretty(&self.token_trees) } } - - fn subtree_replace_token_ids_with_call_site( - subtree: tt::Subtree, - call_site: TokenId, - ) -> tt::Subtree { - tt::Subtree { - delimiter: tt::Delimiter { open: call_site, close: call_site, ..subtree.delimiter }, - token_trees: subtree - .token_trees - .into_iter() - .map(|it| token_tree_replace_token_ids_with_call_site(it, call_site)) - .collect(), - } - } - - fn token_tree_replace_token_ids_with_call_site( - tt: tt::TokenTree, - call_site: TokenId, - ) -> tt::TokenTree { - match tt { - tt::TokenTree::Leaf(leaf) => { - tt::TokenTree::Leaf(leaf_replace_token_ids_with_call_site(leaf, call_site)) - } - tt::TokenTree::Subtree(subtree) => { - tt::TokenTree::Subtree(subtree_replace_token_ids_with_call_site(subtree, call_site)) - } - } - } - - fn leaf_replace_token_ids_with_call_site(leaf: tt::Leaf, call_site: TokenId) -> tt::Leaf { - match leaf { - tt::Leaf::Literal(lit) => tt::Leaf::Literal(tt::Literal { span: call_site, ..lit }), - tt::Leaf::Punct(punct) => tt::Leaf::Punct(tt::Punct { span: call_site, ..punct }), - tt::Leaf::Ident(ident) => tt::Leaf::Ident(tt::Ident { span: call_site, ..ident }), - } - } } -impl TokenStreamBuilder { - pub(super) fn new() -> TokenStreamBuilder { +impl TokenStreamBuilder { + pub(super) fn new() -> TokenStreamBuilder { TokenStreamBuilder { acc: TokenStream::new() } } - pub(super) fn push(&mut self, stream: TokenStream) { + pub(super) fn push(&mut self, stream: TokenStream) { self.acc.extend(stream.into_iter()) } - pub(super) fn build(self) -> TokenStream { + pub(super) fn build(self) -> TokenStream { self.acc } } diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs index b04e3ca19ac1..87d832cc76fa 100644 --- a/crates/proc-macro-srv/src/tests/mod.rs +++ b/crates/proc-macro-srv/src/tests/mod.rs @@ -8,7 +8,7 @@ use expect_test::expect; #[test] fn test_derive_empty() { - assert_expand("DeriveEmpty", r#"struct S;"#, expect!["SUBTREE $$ 1 1"]); + assert_expand("DeriveEmpty", r#"struct S;"#, expect!["SUBTREE $$ 1 1"], expect!["SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"]); } #[test] @@ -23,6 +23,13 @@ fn test_derive_error() { SUBTREE () 1 1 LITERAL "#[derive(DeriveError)] struct S ;" 1 PUNCH ; [alone] 1"##]], + expect![[r##" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT compile_error SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH ! [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + SUBTREE () SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL "#[derive(DeriveError)] struct S ;" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH ; [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"##]], ); } @@ -40,6 +47,15 @@ fn test_fn_like_macro_noop() { LITERAL 1 1 PUNCH , [alone] 1 SUBTREE [] 1 1"#]], + expect![[r#" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT ident SpanData { range: 0..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH , [alone] SpanData { range: 5..6, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 0 SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH , [alone] SpanData { range: 8..9, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 1 SpanData { range: 10..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH , [alone] SpanData { range: 11..12, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + SUBTREE [] SpanData { range: 13..14, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 14..15, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]], ); } @@ -53,6 +69,11 @@ fn test_fn_like_macro_clone_ident_subtree() { IDENT ident 1 PUNCH , [alone] 1 SUBTREE [] 1 1"#]], + expect![[r#" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT ident SpanData { range: 0..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH , [alone] SpanData { range: 5..6, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + SUBTREE [] SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]], ); } @@ -64,6 +85,41 @@ fn test_fn_like_macro_clone_raw_ident() { expect![[r#" SUBTREE $$ 1 1 IDENT r#async 1"#]], + expect![[r#" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT r#async SpanData { range: 0..7, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]], + ); +} + +#[test] +fn test_fn_like_fn_like_span_join() { + assert_expand( + "fn_like_span_join", + "foo bar", + expect![[r#" + SUBTREE $$ 1 1 + IDENT r#joined 1"#]], + expect![[r#" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT r#joined SpanData { range: 0..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]], + ); +} + +#[test] +fn test_fn_like_fn_like_span_ops() { + assert_expand( + "fn_like_span_ops", + "set_def_site resolved_at_def_site start_span", + expect![[r#" + SUBTREE $$ 1 1 + IDENT set_def_site 0 + IDENT resolved_at_def_site 1 + IDENT start_span 1"#]], + expect![[r#" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT set_def_site SpanData { range: 0..150, anchor: SpanAnchor(FileId(41), 1), ctx: SyntaxContextId(0) } + IDENT resolved_at_def_site SpanData { range: 13..33, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT start_span SpanData { range: 34..34, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]], ); } @@ -81,6 +137,15 @@ fn test_fn_like_mk_literals() { LITERAL 3.14 1 LITERAL 123i64 1 LITERAL 123 1"#]], + expect![[r#" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL b"byte_string" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 'c' SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL "string" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 3.14f64 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 3.14 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 123i64 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 123 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]], ); } @@ -93,6 +158,10 @@ fn test_fn_like_mk_idents() { SUBTREE $$ 1 1 IDENT standard 1 IDENT r#raw 1"#]], + expect![[r#" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT standard SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT r#raw SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]], ); } @@ -113,6 +182,18 @@ fn test_fn_like_macro_clone_literals() { LITERAL 3.14f32 1 PUNCH , [alone] 1 LITERAL "hello bridge" 1"#]], + expect![[r#" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 1u16 SpanData { range: 0..4, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH , [alone] SpanData { range: 4..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 2_u32 SpanData { range: 6..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH , [alone] SpanData { range: 11..12, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH - [alone] SpanData { range: 13..14, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 4i64 SpanData { range: 14..18, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH , [alone] SpanData { range: 18..19, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL 3.14f32 SpanData { range: 20..27, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH , [alone] SpanData { range: 27..28, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL "hello bridge" SpanData { range: 29..43, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]], ); } @@ -132,6 +213,13 @@ fn test_attr_macro() { SUBTREE () 1 1 LITERAL "#[attr_error(some arguments)] mod m {}" 1 PUNCH ; [alone] 1"##]], + expect![[r##" + SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + IDENT compile_error SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH ! [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + SUBTREE () SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + LITERAL "#[attr_error(some arguments)] mod m {}" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } + PUNCH ; [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"##]], ); } @@ -147,6 +235,8 @@ fn list_test_macros() { fn_like_clone_tokens [FuncLike] fn_like_mk_literals [FuncLike] fn_like_mk_idents [FuncLike] + fn_like_span_join [FuncLike] + fn_like_span_ops [FuncLike] attr_noop [Attr] attr_panic [Attr] attr_error [Attr] diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs index c12096d140c9..9a1311d9550a 100644 --- a/crates/proc-macro-srv/src/tests/utils.rs +++ b/crates/proc-macro-srv/src/tests/utils.rs @@ -2,47 +2,96 @@ use expect_test::Expect; use proc_macro_api::msg::TokenId; +use span::{ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId}; +use tt::TextRange; use crate::{dylib, proc_macro_test_dylib_path, ProcMacroSrv}; -fn parse_string(code: &str, call_site: TokenId) -> Option { - // This is a bit strange. We need to parse a string into a token stream into - // order to create a tt::SubTree from it in fixtures. `into_subtree` is - // implemented by all the ABIs we have so we arbitrarily choose one ABI to - // write a `parse_string` function for and use that. The tests don't really - // care which ABI we're using as the `into_subtree` function isn't part of - // the ABI and shouldn't change between ABI versions. - crate::server::TokenStream::from_str(code, call_site).ok() +fn parse_string(call_site: TokenId, src: &str) -> crate::server::TokenStream { + crate::server::TokenStream::with_subtree( + mbe::parse_to_token_tree_static_span(call_site, src).unwrap(), + ) } -pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) { - assert_expand_impl(macro_name, ra_fixture, None, expect); +fn parse_string_spanned( + anchor: SpanAnchor, + call_site: SyntaxContextId, + src: &str, +) -> crate::server::TokenStream { + crate::server::TokenStream::with_subtree( + mbe::parse_to_token_tree(anchor, call_site, src).unwrap(), + ) } -pub fn assert_expand_attr(macro_name: &str, ra_fixture: &str, attr_args: &str, expect: Expect) { - assert_expand_impl(macro_name, ra_fixture, Some(attr_args), expect); +pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect, expect_s: Expect) { + assert_expand_impl(macro_name, ra_fixture, None, expect, expect_s); } -fn assert_expand_impl(macro_name: &str, input: &str, attr: Option<&str>, expect: Expect) { +pub fn assert_expand_attr( + macro_name: &str, + ra_fixture: &str, + attr_args: &str, + expect: Expect, + expect_s: Expect, +) { + assert_expand_impl(macro_name, ra_fixture, Some(attr_args), expect, expect_s); +} + +fn assert_expand_impl( + macro_name: &str, + input: &str, + attr: Option<&str>, + expect: Expect, + expect_s: Expect, +) { + let path = proc_macro_test_dylib_path(); + let expander = dylib::Expander::new(&path).unwrap(); + let def_site = TokenId(0); let call_site = TokenId(1); let mixed_site = TokenId(2); - let path = proc_macro_test_dylib_path(); - let expander = dylib::Expander::new(&path).unwrap(); - let fixture = parse_string(input, call_site).unwrap(); - let attr = attr.map(|attr| parse_string(attr, call_site).unwrap().into_subtree(call_site)); + let input_ts = parse_string(call_site, input); + let attr_ts = attr.map(|attr| parse_string(call_site, attr).into_subtree(call_site)); let res = expander .expand( macro_name, - &fixture.into_subtree(call_site), - attr.as_ref(), + input_ts.into_subtree(call_site), + attr_ts, def_site, call_site, mixed_site, ) .unwrap(); expect.assert_eq(&format!("{res:?}")); + + let def_site = Span { + range: TextRange::new(0.into(), 150.into()), + anchor: SpanAnchor { + file_id: FileId::from_raw(41), + ast_id: ErasedFileAstId::from_raw(From::from(1)), + }, + ctx: SyntaxContextId::ROOT, + }; + let call_site = Span { + range: TextRange::new(0.into(), 100.into()), + anchor: SpanAnchor { + file_id: FileId::from_raw(42), + ast_id: ErasedFileAstId::from_raw(From::from(2)), + }, + ctx: SyntaxContextId::ROOT, + }; + let mixed_site = call_site; + + let fixture = parse_string_spanned(call_site.anchor, call_site.ctx, input); + let attr = attr.map(|attr| { + parse_string_spanned(call_site.anchor, call_site.ctx, attr).into_subtree(call_site) + }); + + let res = expander + .expand(macro_name, fixture.into_subtree(call_site), attr, def_site, call_site, mixed_site) + .unwrap(); + expect_s.assert_eq(&format!("{res:?}")); } pub(crate) fn list() -> Vec { diff --git a/crates/proc-macro-test/Cargo.toml b/crates/proc-macro-test/Cargo.toml deleted file mode 100644 index 12d7c07d3ed9..000000000000 --- a/crates/proc-macro-test/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "proc-macro-test" -version = "0.0.0" -publish = false - -authors.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true - -[lib] -doctest = false - -[build-dependencies] -cargo_metadata.workspace = true - -proc-macro-test-impl = { path = "imp", version = "0.0.0" } - -# local deps -toolchain.workspace = true diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 56ce9d11c085..5350023c88fa 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -31,3 +31,6 @@ jemalloc = ["jemalloc-ctl"] # Uncomment to enable for the whole crate graph # default = [ "cpu_profiler" ] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml index 3e48de6456b0..3552ed191628 100644 --- a/crates/project-model/Cargo.toml +++ b/crates/project-model/Cargo.toml @@ -14,8 +14,8 @@ doctest = false [dependencies] anyhow.workspace = true cargo_metadata.workspace = true -rustc-hash = "1.1.0" -semver = "1.0.14" +rustc-hash.workspace = true +semver.workspace = true serde_json.workspace = true serde.workspace = true tracing.workspace = true @@ -33,3 +33,6 @@ toolchain.workspace = true [dev-dependencies] expect-test = "1.4.0" + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index ca3d6e0596ca..d89c4598afc7 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -330,6 +330,7 @@ impl CargoWorkspace { cargo_metadata::Edition::E2015 => Edition::Edition2015, cargo_metadata::Edition::E2018 => Edition::Edition2018, cargo_metadata::Edition::E2021 => Edition::Edition2021, + cargo_metadata::Edition::_E2024 => Edition::Edition2024, _ => { tracing::error!("Unsupported edition `{:?}`", edition); Edition::CURRENT diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index 931eba115766..cf3231498f3e 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -213,6 +213,8 @@ enum EditionData { Edition2018, #[serde(rename = "2021")] Edition2021, + #[serde(rename = "2024")] + Edition2024, } impl From for Edition { @@ -221,6 +223,7 @@ impl From for Edition { EditionData::Edition2015 => Edition::Edition2015, EditionData::Edition2018 => Edition::Edition2018, EditionData::Edition2021 => Edition::Edition2021, + EditionData::Edition2024 => Edition::Edition2024, } } } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 9333570354a0..4057493fa3a6 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -7,7 +7,7 @@ use std::{collections::VecDeque, fmt, fs, iter, process::Command, str::FromStr, use anyhow::{format_err, Context}; use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, DependencyKind, - Edition, Env, FileId, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, TargetLayoutLoadResult, + Edition, Env, FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, }; use cfg::{CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; @@ -619,7 +619,7 @@ impl ProjectWorkspace { sysroot.as_ref().ok(), extra_env, Err("rust-project.json projects have no target layout set".into()), - toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())), + toolchain.clone(), ) } ProjectWorkspace::Cargo { @@ -644,7 +644,7 @@ impl ProjectWorkspace { Ok(it) => Ok(Arc::from(it.as_str())), Err(it) => Err(Arc::from(it.as_str())), }, - toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())), + toolchain.as_ref(), ), ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { detached_files_to_crate_graph( @@ -733,7 +733,7 @@ fn project_json_to_crate_graph( sysroot: Option<&Sysroot>, extra_env: &FxHashMap, target_layout: TargetLayoutLoadResult, - channel: Option, + toolchain: Option, ) -> (CrateGraph, ProcMacroPaths) { let mut res = (CrateGraph::default(), ProcMacroPaths::default()); let (crate_graph, proc_macros) = &mut res; @@ -744,7 +744,7 @@ fn project_json_to_crate_graph( rustc_cfg.clone(), target_layout.clone(), load, - channel, + toolchain.as_ref(), ) }); @@ -807,7 +807,7 @@ fn project_json_to_crate_graph( CrateOrigin::Local { repo: None, name: None } }, target_layout.clone(), - channel, + toolchain.clone(), ); if *is_proc_macro { if let Some(path) = proc_macro_dylib_path.clone() { @@ -853,7 +853,7 @@ fn cargo_to_crate_graph( forced_cfg: Option, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, - channel: Option, + toolchain: Option<&Version>, ) -> (CrateGraph, ProcMacroPaths) { let _p = profile::span("cargo_to_crate_graph"); let mut res = (CrateGraph::default(), ProcMacroPaths::default()); @@ -866,7 +866,7 @@ fn cargo_to_crate_graph( rustc_cfg.clone(), target_layout.clone(), load, - channel, + toolchain, ), None => (SysrootPublicDeps::default(), None), }; @@ -950,7 +950,7 @@ fn cargo_to_crate_graph( is_proc_macro, target_layout.clone(), false, - channel, + toolchain.cloned(), ); if kind == TargetKind::Lib { lib_tgt = Some((crate_id, name.clone())); @@ -1038,7 +1038,7 @@ fn cargo_to_crate_graph( rustc_build_scripts }, target_layout, - channel, + toolchain, ); } } @@ -1117,7 +1117,7 @@ fn handle_rustc_crates( override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, - channel: Option, + toolchain: Option<&Version>, ) { let mut rustc_pkg_crates = FxHashMap::default(); // The root package of the rustc-dev component is rustc_driver, so we match that @@ -1172,7 +1172,7 @@ fn handle_rustc_crates( rustc_workspace[tgt].is_proc_macro, target_layout.clone(), true, - channel, + toolchain.cloned(), ); pkg_to_lib_crate.insert(pkg, crate_id); // Add dependencies on core / std / alloc for this crate @@ -1248,7 +1248,7 @@ fn add_target_crate_root( is_proc_macro: bool, target_layout: TargetLayoutLoadResult, rustc_crate: bool, - channel: Option, + toolchain: Option, ) -> CrateId { let edition = pkg.edition; let potential_cfg_options = if pkg.features.is_empty() { @@ -1304,7 +1304,7 @@ fn add_target_crate_root( CrateOrigin::Library { repo: pkg.repository.clone(), name: pkg.name.clone() } }, target_layout, - channel, + toolchain, ); if is_proc_macro { let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) { @@ -1346,7 +1346,7 @@ fn sysroot_to_crate_graph( rustc_cfg: Vec, target_layout: TargetLayoutLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, - channel: Option, + toolchain: Option<&Version>, ) -> (SysrootPublicDeps, Option) { let _p = profile::span("sysroot_to_crate_graph"); let cfg_options = create_cfg_options(rustc_cfg.clone()); @@ -1357,7 +1357,7 @@ fn sysroot_to_crate_graph( rustc_cfg, cfg_options, target_layout, - channel, + toolchain, crate_graph, sysroot, ), @@ -1380,7 +1380,7 @@ fn sysroot_to_crate_graph( false, CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), target_layout.clone(), - channel, + toolchain.cloned(), ); Some((krate, crate_id)) }) @@ -1412,7 +1412,7 @@ fn handle_hack_cargo_workspace( rustc_cfg: Vec, cfg_options: CfgOptions, target_layout: Result, Arc>, - channel: Option, + toolchain: Option<&Version>, crate_graph: &mut CrateGraph, sysroot: &Sysroot, ) -> FxHashMap { @@ -1426,7 +1426,7 @@ fn handle_hack_cargo_workspace( Some(cfg_options), &WorkspaceBuildScripts::default(), target_layout, - channel, + toolchain, ); crate_graph.extend(cg, &mut pm); for crate_name in ["std", "alloc", "core"] { diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index e98f016ca7df..d8d9e559e5c1 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -62,7 +62,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 1: CrateData { root_file_id: FileId( @@ -135,7 +135,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 2: CrateData { root_file_id: FileId( @@ -208,7 +208,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 3: CrateData { root_file_id: FileId( @@ -281,7 +281,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 4: CrateData { root_file_id: FileId( @@ -350,6 +350,6 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, } \ No newline at end of file diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index e98f016ca7df..d8d9e559e5c1 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -62,7 +62,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 1: CrateData { root_file_id: FileId( @@ -135,7 +135,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 2: CrateData { root_file_id: FileId( @@ -208,7 +208,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 3: CrateData { root_file_id: FileId( @@ -281,7 +281,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 4: CrateData { root_file_id: FileId( @@ -350,6 +350,6 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, } \ No newline at end of file diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index 7ecd53572e2f..e0ba5ed498fa 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -61,7 +61,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 1: CrateData { root_file_id: FileId( @@ -133,7 +133,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 2: CrateData { root_file_id: FileId( @@ -205,7 +205,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 3: CrateData { root_file_id: FileId( @@ -277,7 +277,7 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, 4: CrateData { root_file_id: FileId( @@ -346,6 +346,6 @@ target_layout: Err( "target_data_layout not loaded", ), - channel: None, + toolchain: None, }, } \ No newline at end of file diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index 581a6afc148f..e35f0fc7327e 100644 --- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -39,7 +39,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 1: CrateData { root_file_id: FileId( @@ -72,7 +72,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 2: CrateData { root_file_id: FileId( @@ -105,7 +105,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 3: CrateData { root_file_id: FileId( @@ -138,7 +138,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 4: CrateData { root_file_id: FileId( @@ -188,7 +188,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 5: CrateData { root_file_id: FileId( @@ -221,7 +221,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 6: CrateData { root_file_id: FileId( @@ -319,7 +319,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 7: CrateData { root_file_id: FileId( @@ -352,7 +352,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 8: CrateData { root_file_id: FileId( @@ -385,7 +385,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 9: CrateData { root_file_id: FileId( @@ -418,7 +418,7 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, 10: CrateData { root_file_id: FileId( @@ -495,6 +495,6 @@ target_layout: Err( "rust-project.json projects have no target layout set", ), - channel: None, + toolchain: None, }, } \ No newline at end of file diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 39ac338aa1a9..ad24d6d28cd7 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -24,12 +24,12 @@ crossbeam-channel = "0.5.5" dissimilar.workspace = true itertools.workspace = true scip = "0.3.1" -lsp-types = { version = "=0.94.0", features = ["proposed"] } +lsp-types = { version = "=0.95.0", features = ["proposed"] } parking_lot = "0.12.1" xflags = "0.3.0" oorandom = "11.1.3" rayon.workspace = true -rustc-hash = "1.1.0" +rustc-hash.workspace = true serde_json = { workspace = true, features = ["preserve_order"] } serde.workspace = true num_cpus = "1.15.0" @@ -76,6 +76,7 @@ expect-test = "1.4.0" xshell.workspace = true test-utils.workspace = true +test-fixture.workspace = true sourcegen.workspace = true mbe.workspace = true @@ -93,3 +94,6 @@ in-rust-tree = [ "hir-def/in-rust-tree", "hir-ty/in-rust-tree", ] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 8472e49de983..6f40a4c88ed8 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -172,7 +172,15 @@ fn run_server() -> anyhow::Result<()> { let (connection, io_threads) = Connection::stdio(); - let (initialize_id, initialize_params) = connection.initialize_start()?; + let (initialize_id, initialize_params) = match connection.initialize_start() { + Ok(it) => it, + Err(e) => { + if e.channel_is_disconnected() { + io_threads.join()?; + } + return Err(e.into()); + } + }; tracing::info!("InitializeParams: {}", initialize_params); let lsp_types::InitializeParams { root_uri, @@ -240,7 +248,12 @@ fn run_server() -> anyhow::Result<()> { let initialize_result = serde_json::to_value(initialize_result).unwrap(); - connection.initialize_finish(initialize_id, initialize_result)?; + if let Err(e) = connection.initialize_finish(initialize_id, initialize_result) { + if e.channel_is_disconnected() { + io_threads.join()?; + } + return Err(e.into()); + } if !config.has_linked_projects() && config.detached_files().is_empty() { config.rediscover_workspaces(); diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 8c9261ab05ee..94eab97e8fcd 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -157,6 +157,8 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities { "ssr": true, "workspaceSymbolScopeKindFiltering": true, })), + diagnostic_provider: None, + inline_completion_provider: None, } } diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 728bade0d0a5..0190ca3cab85 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -209,7 +209,7 @@ mod tests { use super::*; use cfg::CfgExpr; - use mbe::{syntax_node_to_token_tree, DummyTestSpanMap}; + use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; use syntax::{ ast::{self, AstNode}, SmolStr, @@ -219,7 +219,7 @@ mod tests { let cfg_expr = { let source_file = ast::SourceFile::parse(cfg).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), &DummyTestSpanMap); + let tt = syntax_node_to_token_tree(tt.syntax(), &DummyTestSpanMap, DUMMY); CfgExpr::parse(&tt) }; diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index c89b88ac0f9e..b8f6138161e5 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -4,8 +4,8 @@ use std::{ cell::RefCell, collections::HashMap, fs::read_to_string, panic::AssertUnwindSafe, path::PathBuf, }; -use hir::Crate; -use ide::{AnalysisHost, Change, DiagnosticCode, DiagnosticsConfig}; +use hir::{Change, Crate}; +use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig}; use profile::StopWatch; use project_model::{CargoConfig, ProjectWorkspace, RustLibSource, Sysroot}; diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 30e11402cd8d..95c8798d43c8 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -278,8 +278,8 @@ fn token_to_symbol(token: &TokenStaticData) -> Option { mod test { use super::*; use ide::{AnalysisHost, FilePosition, StaticIndex, TextSize}; - use ide_db::base_db::fixture::ChangeFixture; use scip::symbol::format_symbol; + use test_fixture::ChangeFixture; fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) { let mut host = AnalysisHost::default(); diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 258f74106395..88fb3708449f 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -7,7 +7,11 @@ //! configure the server itself, feature flags are passed into analysis, and //! tweak things like automatic insertion of `()` in completions. -use std::{fmt, iter, ops::Not, path::PathBuf}; +use std::{ + fmt, iter, + ops::Not, + path::{Path, PathBuf}, +}; use cfg::{CfgAtom, CfgDiff}; use flycheck::FlycheckConfig; @@ -105,6 +109,9 @@ config_data! { /// ``` /// . cargo_buildScripts_overrideCommand: Option> = "null", + /// Rerun proc-macros building/build-scripts running when proc-macro + /// or build-script sources change and are saved. + cargo_buildScripts_rebuildOnSave: bool = "false", /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to /// avoid checking unnecessary things. cargo_buildScripts_useRustcWrapper: bool = "true", @@ -164,15 +171,15 @@ config_data! { /// Specifies the working directory for running checks. /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories. // FIXME: Ideally we would support this in some way - /// This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`. + /// This falls back to "root" if `#rust-analyzer.check.invocationStrategy#` is set to `once`. /// - "root": run checks in the project's root directory. - /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` + /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` /// is set. check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"", /// Specifies the invocation strategy to use when running the check command. /// If `per_workspace` is set, the command will be executed for each workspace. /// If `once` is set, the command will be executed once. - /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` + /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` /// is set. check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"", /// Whether to pass `--no-default-features` to Cargo. Defaults to @@ -191,8 +198,8 @@ config_data! { /// If there are multiple linked projects/workspaces, this command is invoked for /// each of them, with the working directory being the workspace root /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten - /// by changing `#rust-analyzer.cargo.check.invocationStrategy#` and - /// `#rust-analyzer.cargo.check.invocationLocation#`. + /// by changing `#rust-analyzer.check.invocationStrategy#` and + /// `#rust-analyzer.check.invocationLocation#`. /// /// An example command would be: /// @@ -917,7 +924,19 @@ impl Config { pub fn has_linked_projects(&self) -> bool { !self.data.linkedProjects.is_empty() } - pub fn linked_projects(&self) -> Vec { + pub fn linked_manifests(&self) -> impl Iterator + '_ { + self.data.linkedProjects.iter().filter_map(|it| match it { + ManifestOrProjectJson::Manifest(p) => Some(&**p), + ManifestOrProjectJson::ProjectJson(_) => None, + }) + } + pub fn has_linked_project_jsons(&self) -> bool { + self.data + .linkedProjects + .iter() + .any(|it| matches!(it, ManifestOrProjectJson::ProjectJson(_))) + } + pub fn linked_or_discovered_projects(&self) -> Vec { match self.data.linkedProjects.as_slice() { [] => { let exclude_dirs: Vec<_> = @@ -952,15 +971,6 @@ impl Config { } } - pub fn add_linked_projects(&mut self, linked_projects: Vec) { - let mut linked_projects = linked_projects - .into_iter() - .map(ManifestOrProjectJson::ProjectJson) - .collect::>(); - - self.data.linkedProjects.append(&mut linked_projects); - } - pub fn did_save_text_document_dynamic_registration(&self) -> bool { let caps = try_or_def!(self.caps.text_document.as_ref()?.synchronization.clone()?); caps.did_save == Some(true) && caps.dynamic_registration == Some(true) @@ -1369,6 +1379,10 @@ impl Config { self.data.checkOnSave } + pub fn script_rebuild_on_save(&self) -> bool { + self.data.cargo_buildScripts_rebuildOnSave + } + pub fn runnables(&self) -> RunnablesConfig { RunnablesConfig { override_cargo: self.data.runnables_command.clone(), diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 0f31fe16054a..f57a27305f03 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -7,7 +7,8 @@ use std::time::Instant; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; -use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId}; +use hir::Change; +use ide::{Analysis, AnalysisHost, Cancellable, FileId}; use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase}; use load_cargo::SourceRootConfig; use lsp_types::{SemanticTokens, Url}; diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index f9070d273539..7e6219991b66 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -130,6 +130,13 @@ pub(crate) fn handle_did_save_text_document( state: &mut GlobalState, params: DidSaveTextDocumentParams, ) -> anyhow::Result<()> { + if state.config.script_rebuild_on_save() && state.proc_macro_changed { + // reset the flag + state.proc_macro_changed = false; + // rebuild the proc macros + state.fetch_build_data_queue.request_op("ScriptRebuildOnSave".to_owned(), ()); + } + if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { // Re-fetch workspaces if a workspace related file has changed if let Some(abs_path) = vfs_path.as_path() { diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index d8a590c80884..f1317ce2b401 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -453,7 +453,7 @@ pub(crate) fn handle_document_symbol( pub(crate) fn handle_workspace_symbol( snap: GlobalStateSnapshot, params: WorkspaceSymbolParams, -) -> anyhow::Result>> { +) -> anyhow::Result> { let _p = profile::span("handle_workspace_symbol"); let config = snap.config.workspace_symbol(); @@ -479,7 +479,7 @@ pub(crate) fn handle_workspace_symbol( res = exec_query(&snap, query)?; } - return Ok(Some(res)); + return Ok(Some(lsp_types::WorkspaceSymbolResponse::Nested(res))); fn decide_search_scope_and_kind( params: &WorkspaceSymbolParams, @@ -519,13 +519,12 @@ pub(crate) fn handle_workspace_symbol( fn exec_query( snap: &GlobalStateSnapshot, query: Query, - ) -> anyhow::Result> { + ) -> anyhow::Result> { let mut res = Vec::new(); for nav in snap.analysis.symbol_search(query)? { let container_name = nav.container_name.as_ref().map(|v| v.to_string()); - #[allow(deprecated)] - let info = SymbolInformation { + let info = lsp_types::WorkspaceSymbol { name: match &nav.alias { Some(alias) => format!("{} (alias for {})", alias, nav.name), None => format!("{}", nav.name), @@ -534,10 +533,11 @@ pub(crate) fn handle_workspace_symbol( .kind .map(to_proto::symbol_kind) .unwrap_or(lsp_types::SymbolKind::VARIABLE), + // FIXME: Set deprecation tags: None, - location: to_proto::location_from_nav(snap, nav)?, container_name, - deprecated: None, + location: lsp_types::OneOf::Left(to_proto::location_from_nav(snap, nav)?), + data: None, }; res.push(info); } @@ -801,7 +801,7 @@ pub(crate) fn handle_runnables( } } None => { - if !snap.config.linked_projects().is_empty() { + if !snap.config.linked_or_discovered_projects().is_empty() { res.push(lsp_ext::Runnable { label: "cargo check --workspace".to_string(), location: None, diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 41ff17f5e438..d94f7cefa60e 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -10,7 +10,8 @@ //! in release mode in VS Code. There's however "rust-analyzer: Copy Run Command Line" //! which you can use to paste the command in terminal and add `--release` manually. -use ide::{CallableSnippets, Change, CompletionConfig, FilePosition, TextSize}; +use hir::Change; +use ide::{CallableSnippets, CompletionConfig, FilePosition, TextSize}; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig}, SnippetCap, diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs index ad56899163d3..35c8fad37415 100644 --- a/crates/rust-analyzer/src/lsp/ext.rs +++ b/crates/rust-analyzer/src/lsp/ext.rs @@ -627,7 +627,7 @@ pub enum WorkspaceSymbol {} impl Request for WorkspaceSymbol { type Params = WorkspaceSymbolParams; - type Result = Option>; + type Result = Option; const METHOD: &'static str = "workspace/symbol"; } diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index dae560c5de12..7f3c3aa7a15b 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -857,7 +857,7 @@ pub(crate) fn location_from_nav( ) -> Cancellable { let url = url(snap, nav.file_id); let line_index = snap.file_line_index(nav.file_id)?; - let range = range(&line_index, nav.full_range); + let range = range(&line_index, nav.focus_or_full_range()); let loc = lsp_types::Location::new(url, range); Ok(loc) } diff --git a/crates/rust-analyzer/src/lsp/utils.rs b/crates/rust-analyzer/src/lsp/utils.rs index b388b317599a..a4417e4d4a14 100644 --- a/crates/rust-analyzer/src/lsp/utils.rs +++ b/crates/rust-analyzer/src/lsp/utils.rs @@ -171,30 +171,19 @@ pub(crate) fn apply_document_changes( file_contents: impl FnOnce() -> String, mut content_changes: Vec, ) -> String { - // Skip to the last full document change, as it invalidates all previous changes anyways. - let mut start = content_changes - .iter() - .rev() - .position(|change| change.range.is_none()) - .map(|idx| content_changes.len() - idx - 1) - .unwrap_or(0); - - let mut text: String = match content_changes.get_mut(start) { - // peek at the first content change as an optimization - Some(lsp_types::TextDocumentContentChangeEvent { range: None, text, .. }) => { - let text = mem::take(text); - start += 1; - - // The only change is a full document update - if start == content_changes.len() { - return text; + // If at least one of the changes is a full document change, use the last + // of them as the starting point and ignore all previous changes. + let (mut text, content_changes) = + match content_changes.iter().rposition(|change| change.range.is_none()) { + Some(idx) => { + let text = mem::take(&mut content_changes[idx].text); + (text, &content_changes[idx + 1..]) } - text - } - Some(_) => file_contents(), - // we received no content changes - None => return file_contents(), - }; + None => (file_contents(), &content_changes[..]), + }; + if content_changes.is_empty() { + return text; + } let mut line_index = LineIndex { // the index will be overwritten in the bottom loop's first iteration diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 7ab528f49751..91dc6c2e4b41 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -16,10 +16,9 @@ use std::{iter, mem}; use flycheck::{FlycheckConfig, FlycheckHandle}; -use hir::db::DefDatabase; -use ide::Change; +use hir::{db::DefDatabase, Change, ProcMacros}; use ide_db::{ - base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, ProcMacros}, + base_db::{salsa::Durability, CrateGraph, ProcMacroPaths}, FxHashMap, }; use itertools::Itertools; @@ -81,7 +80,8 @@ impl GlobalState { &self.config.lru_query_capacities().cloned().unwrap_or_default(), ); } - if self.config.linked_projects() != old_config.linked_projects() { + if self.config.linked_or_discovered_projects() != old_config.linked_or_discovered_projects() + { self.fetch_workspaces_queue.request_op("linked projects changed".to_string(), false) } else if self.config.flycheck() != old_config.flycheck() { self.reload_flycheck(); @@ -129,7 +129,7 @@ impl GlobalState { status.health = lsp_ext::Health::Warning; message.push_str("Auto-reloading is disabled and the workspace has changed, a manual workspace reload is required.\n\n"); } - if self.config.linked_projects().is_empty() + if self.config.linked_or_discovered_projects().is_empty() && self.config.detached_files().is_empty() && self.config.notifications().cargo_toml_not_found { @@ -175,7 +175,21 @@ impl GlobalState { if let Err(_) = self.fetch_workspace_error() { status.health = lsp_ext::Health::Error; - message.push_str("Failed to load workspaces.\n\n"); + message.push_str("Failed to load workspaces."); + + if self.config.has_linked_projects() { + message.push_str( + "`rust-analyzer.linkedProjects` have been specified, which may be incorrect. Specified project paths:\n", + ); + message.push_str(&format!( + " {}", + self.config.linked_manifests().map(|it| it.display()).format("\n ") + )); + if self.config.has_linked_project_jsons() { + message.push_str("\nAdditionally, one or more project jsons are specified") + } + } + message.push_str("\n\n"); } if !message.is_empty() { @@ -188,7 +202,7 @@ impl GlobalState { tracing::info!(%cause, "will fetch workspaces"); self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, { - let linked_projects = self.config.linked_projects(); + let linked_projects = self.config.linked_or_discovered_projects(); let detached_files = self.config.detached_files().to_vec(); let cargo_config = self.config.cargo(); diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index ec8e5c6dd968..78411e2d58d0 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -832,7 +832,7 @@ fn main() { } #[test] -#[cfg(feature = "sysroot-abi")] +#[cfg(any(feature = "sysroot-abi", rust_analyzer))] fn resolve_proc_macro() { use expect_test::expect; if skip_slow_tests() { diff --git a/crates/rustc-dependencies/Cargo.toml b/crates/rustc-dependencies/Cargo.toml index 1b3b6ec735e7..0bf04301df16 100644 --- a/crates/rustc-dependencies/Cargo.toml +++ b/crates/rustc-dependencies/Cargo.toml @@ -18,3 +18,6 @@ ra-ap-rustc_abi = { version = "0.21.0", default-features = false } [features] in-rust-tree = [] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/sourcegen/Cargo.toml b/crates/sourcegen/Cargo.toml index 0514af8e7839..d5ea4c39aa17 100644 --- a/crates/sourcegen/Cargo.toml +++ b/crates/sourcegen/Cargo.toml @@ -2,6 +2,7 @@ name = "sourcegen" version = "0.0.0" description = "TBD" +publish = false authors.workspace = true edition.workspace = true @@ -13,3 +14,6 @@ doctest = false [dependencies] xshell.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/span/Cargo.toml b/crates/span/Cargo.toml new file mode 100644 index 000000000000..69b88b5a17b5 --- /dev/null +++ b/crates/span/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "span" +version = "0.0.0" +rust-version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + + +[dependencies] +la-arena.workspace = true +rust-analyzer-salsa.workspace = true + + +# local deps +vfs.workspace = true +syntax.workspace = true +stdx.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/base-db/src/span.rs b/crates/span/src/lib.rs similarity index 72% rename from crates/base-db/src/span.rs rename to crates/span/src/lib.rs index d8990eb7cae0..7617acde64a2 100644 --- a/crates/base-db/src/span.rs +++ b/crates/span/src/lib.rs @@ -1,10 +1,28 @@ //! File and span related types. -// FIXME: This should probably be moved into its own crate. +// FIXME: This should be moved into its own crate to get rid of the dependency inversion, base-db +// has business depending on tt, tt should depend on a span crate only (which unforunately will have +// to depend on salsa) use std::fmt; use salsa::InternId; -use tt::SyntaxContext; -use vfs::FileId; + +mod map; + +pub use crate::map::{RealSpanMap, SpanMap}; +pub use syntax::{TextRange, TextSize}; +pub use vfs::FileId; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct FilePosition { + pub file_id: FileId, + pub offset: TextSize, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct FileRange { + pub file_id: FileId, + pub range: TextRange, +} pub type ErasedFileAstId = la_arena::Idx; @@ -12,7 +30,33 @@ pub type ErasedFileAstId = la_arena::Idx; pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId = la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(0)); -pub type SpanData = tt::SpanData; +/// FileId used as the span for syntax node fixups. Any Span containing this file id is to be +/// considered fake. +pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId = + // we pick the second to last for this in case we every consider making this a NonMaxU32, this + // is required to be stable for the proc-macro-server + la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(!0 - 1)); + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct SpanData { + /// The text range of this span, relative to the anchor. + /// We need the anchor for incrementality, as storing absolute ranges will require + /// recomputation on every change in a file at all times. + pub range: TextRange, + pub anchor: SpanAnchor, + /// The syntax context of the span. + pub ctx: Ctx, +} +impl Span { + #[deprecated = "dummy spans will panic if surfaced incorrectly, as such they should be replaced appropriately"] + pub const DUMMY: Self = SpanData { + range: TextRange::empty(TextSize::new(0)), + anchor: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID }, + ctx: SyntaxContextId::ROOT, + }; +} + +pub type Span = SpanData; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SyntaxContextId(InternId); @@ -33,7 +77,15 @@ impl fmt::Debug for SyntaxContextId { } } } -crate::impl_intern_key!(SyntaxContextId); + +impl salsa::InternKey for SyntaxContextId { + fn from_intern_id(v: salsa::InternId) -> Self { + SyntaxContextId(v) + } + fn as_intern_id(&self) -> salsa::InternId { + self.0 + } +} impl fmt::Display for SyntaxContextId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -41,9 +93,6 @@ impl fmt::Display for SyntaxContextId { } } -impl SyntaxContext for SyntaxContextId { - const DUMMY: Self = Self::ROOT; -} // inherent trait impls please tyvm impl SyntaxContextId { pub const ROOT: Self = SyntaxContextId(unsafe { InternId::new_unchecked(0) }); @@ -55,6 +104,14 @@ impl SyntaxContextId { pub fn is_root(self) -> bool { self == Self::ROOT } + + pub fn into_u32(self) -> u32 { + self.0.as_u32() + } + + pub fn from_u32(u32: u32) -> Self { + Self(InternId::from(u32)) + } } #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -69,10 +126,6 @@ impl fmt::Debug for SpanAnchor { } } -impl tt::SpanAnchor for SpanAnchor { - const DUMMY: Self = SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID }; -} - /// Input to the analyzer is a set of files, where each file is identified by /// `FileId` and contains source code. However, another source of source code in /// Rust are macros: each macro can be thought of as producing a "temporary @@ -90,6 +143,7 @@ impl tt::SpanAnchor for SpanAnchor { /// The two variants are encoded in a single u32 which are differentiated by the MSB. /// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a /// `MacroCallId`. +// FIXME: Give this a better fitting name #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct HirFileId(u32); @@ -120,7 +174,15 @@ pub struct MacroFileId { /// `println!("Hello, {}", world)`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MacroCallId(salsa::InternId); -crate::impl_intern_key!(MacroCallId); + +impl salsa::InternKey for MacroCallId { + fn from_intern_id(v: salsa::InternId) -> Self { + MacroCallId(v) + } + fn as_intern_id(&self) -> salsa::InternId { + self.0 + } +} impl MacroCallId { pub fn as_file(self) -> HirFileId { diff --git a/crates/mbe/src/token_map.rs b/crates/span/src/map.rs similarity index 59% rename from crates/mbe/src/token_map.rs rename to crates/span/src/map.rs index 7d15812f8cb6..d69df91b63ef 100644 --- a/crates/mbe/src/token_map.rs +++ b/crates/span/src/map.rs @@ -1,18 +1,23 @@ -//! Mapping between `TokenId`s and the token's position in macro definitions or inputs. +//! A map that maps a span to every position in a file. Usually maps a span to some range of positions. +//! Allows bidirectional lookup. use std::hash::Hash; use stdx::{always, itertools::Itertools}; use syntax::{TextRange, TextSize}; -use tt::Span; +use vfs::FileId; + +use crate::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; /// Maps absolute text ranges for the corresponding file to the relevant span data. #[derive(Debug, PartialEq, Eq, Clone, Hash)] -pub struct SpanMap { +pub struct SpanMap { spans: Vec<(TextSize, S)>, + // FIXME: Should be + // spans: Vec<(TextSize, crate::SyntaxContextId)>, } -impl SpanMap { +impl SpanMap { /// Creates a new empty [`SpanMap`]. pub fn empty() -> Self { Self { spans: Vec::new() } @@ -44,7 +49,10 @@ impl SpanMap { /// Returns all [`TextRange`]s that correspond to the given span. /// /// Note this does a linear search through the entire backing vector. - pub fn ranges_with_span(&self, span: S) -> impl Iterator + '_ { + pub fn ranges_with_span(&self, span: S) -> impl Iterator + '_ + where + S: Eq, + { // FIXME: This should ignore the syntax context! self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| { if s != span { @@ -74,3 +82,50 @@ impl SpanMap { self.spans.iter().copied() } } + +#[derive(PartialEq, Eq, Hash, Debug)] +pub struct RealSpanMap { + file_id: FileId, + /// Invariant: Sorted vec over TextSize + // FIXME: SortedVec<(TextSize, ErasedFileAstId)>? + pairs: Box<[(TextSize, ErasedFileAstId)]>, + end: TextSize, +} + +impl RealSpanMap { + /// Creates a real file span map that returns absolute ranges (relative ranges to the root ast id). + pub fn absolute(file_id: FileId) -> Self { + RealSpanMap { + file_id, + pairs: Box::from([(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)]), + end: TextSize::new(!0), + } + } + + pub fn from_file( + file_id: FileId, + pairs: Box<[(TextSize, ErasedFileAstId)]>, + end: TextSize, + ) -> Self { + Self { file_id, pairs, end } + } + + pub fn span_for_range(&self, range: TextRange) -> Span { + assert!( + range.end() <= self.end, + "range {range:?} goes beyond the end of the file {:?}", + self.end + ); + let start = range.start(); + let idx = self + .pairs + .binary_search_by(|&(it, _)| it.cmp(&start).then(std::cmp::Ordering::Less)) + .unwrap_err(); + let (offset, ast_id) = self.pairs[idx - 1]; + Span { + range: range - offset, + anchor: SpanAnchor { file_id: self.file_id, ast_id }, + ctx: SyntaxContextId::ROOT, + } + } +} diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index c914ae2144b5..e6014cf812e5 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -27,3 +27,6 @@ winapi = { version = "0.3.9", features = ["winerror"] } [features] # Uncomment to enable for the whole crate graph # default = [ "backtrace" ] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 7a7c0d267fed..40a93fec2cec 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -17,7 +17,7 @@ cov-mark = "2.0.0-pre.1" either.workspace = true itertools.workspace = true rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash.workspace = true once_cell = "1.17.0" indexmap.workspace = true smol_str.workspace = true @@ -42,3 +42,6 @@ sourcegen.workspace = true [features] in-rust-tree = ["rustc-dependencies/in-rust-tree"] + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/syntax/fuzz/Cargo.toml b/crates/syntax/fuzz/Cargo.toml index 6070222f1f19..ebf538aa2471 100644 --- a/crates/syntax/fuzz/Cargo.toml +++ b/crates/syntax/fuzz/Cargo.toml @@ -24,3 +24,6 @@ path = "fuzz_targets/parser.rs" [[bin]] name = "reparse" path = "fuzz_targets/reparse.rs" + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 37d8212042da..4c2878f49f0e 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -13,7 +13,7 @@ use crate::{ SyntaxNode, SyntaxToken, }; -use super::{HasArgList, HasName}; +use super::{GenericParam, HasArgList, HasName}; pub trait GenericParamsOwnerEdit: ast::HasGenericParams { fn get_or_create_generic_param_list(&self) -> ast::GenericParamList; @@ -272,6 +272,36 @@ impl ast::GenericParamList { } } + /// Find the params corresponded to generic arg + pub fn find_generic_arg(&self, generic_arg: &ast::GenericArg) -> Option { + self.generic_params().find_map(move |param| match (¶m, &generic_arg) { + (ast::GenericParam::LifetimeParam(a), ast::GenericArg::LifetimeArg(b)) => { + (a.lifetime()?.lifetime_ident_token()?.text() + == b.lifetime()?.lifetime_ident_token()?.text()) + .then_some(param) + } + (ast::GenericParam::TypeParam(a), ast::GenericArg::TypeArg(b)) => { + debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token()); + (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param) + } + (ast::GenericParam::ConstParam(a), ast::GenericArg::TypeArg(b)) => { + debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token()); + (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param) + } + _ => None, + }) + } + + /// Removes the corresponding generic arg + pub fn remove_generic_arg(&self, generic_arg: &ast::GenericArg) -> Option { + let param_to_remove = self.find_generic_arg(generic_arg); + + if let Some(param) = ¶m_to_remove { + self.remove_generic_param(param.clone()); + } + param_to_remove + } + /// Constructs a matching [`ast::GenericArgList`] pub fn to_generic_args(&self) -> ast::GenericArgList { let args = self.generic_params().filter_map(|param| match param { @@ -300,6 +330,20 @@ impl ast::WhereClause { } ted::append_child(self.syntax(), predicate.syntax()); } + + pub fn remove_predicate(&self, predicate: ast::WherePred) { + if let Some(previous) = predicate.syntax().prev_sibling() { + if let Some(next_token) = previous.next_sibling_or_token() { + ted::remove_all(next_token..=predicate.syntax().clone().into()); + } + } else if let Some(next) = predicate.syntax().next_sibling() { + if let Some(next_token) = next.prev_sibling_or_token() { + ted::remove_all(predicate.syntax().clone().into()..=next_token); + } + } else { + ted::remove(predicate.syntax()); + } + } } impl ast::TypeParam { @@ -414,6 +458,7 @@ impl ast::UseTree { u.remove_recursive(); } } + u.remove_unnecessary_braces(); } } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index ad63cc558629..2abbfc81f675 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -207,10 +207,28 @@ fn merge_gen_params( (None, Some(bs)) => Some(bs), (Some(ps), None) => Some(ps), (Some(ps), Some(bs)) => { - for b in bs.generic_params() { - ps.add_generic_param(b); - } - Some(ps) + // make sure lifetime is placed before other generic params + let generic_params = ps.generic_params().merge_by(bs.generic_params(), |_, b| { + !matches!(b, ast::GenericParam::LifetimeParam(_)) + }); + Some(generic_param_list(generic_params)) + } + } +} + +fn merge_where_clause( + ps: Option, + bs: Option, +) -> Option { + match (ps, bs) { + (None, None) => None, + (None, Some(bs)) => Some(bs), + (Some(ps), None) => Some(ps), + (Some(ps), Some(bs)) => { + let preds = where_clause(std::iter::empty()).clone_for_update(); + ps.predicates().for_each(|p| preds.add_predicate(p)); + bs.predicates().for_each(|p| preds.add_predicate(p)); + Some(preds) } } } @@ -251,9 +269,9 @@ pub fn impl_( pub fn impl_trait( is_unsafe: bool, trait_gen_params: Option, - trait_gen_args: Option, + trait_gen_args: Option, type_gen_params: Option, - type_gen_args: Option, + type_gen_args: Option, is_negative: bool, path_type: ast::Type, ty: ast::Type, @@ -262,15 +280,9 @@ pub fn impl_trait( body: Option>>, ) -> ast::Impl { let is_unsafe = if is_unsafe { "unsafe " } else { "" }; - let ty_gen_args = match merge_gen_params(type_gen_params.clone(), type_gen_args) { - Some(pars) => pars.to_generic_args().to_string(), - None => String::new(), - }; - let tr_gen_args = match merge_gen_params(trait_gen_params.clone(), trait_gen_args) { - Some(pars) => pars.to_generic_args().to_string(), - None => String::new(), - }; + let trait_gen_args = trait_gen_args.map(|args| args.to_string()).unwrap_or_default(); + let type_gen_args = type_gen_args.map(|args| args.to_string()).unwrap_or_default(); let gen_params = match merge_gen_params(trait_gen_params, type_gen_params) { Some(pars) => pars.to_string(), @@ -279,25 +291,15 @@ pub fn impl_trait( let is_negative = if is_negative { "! " } else { "" }; - let where_clause = match (ty_where_clause, trait_where_clause) { - (None, None) => " ".to_string(), - (None, Some(tr)) => format!("\n{}\n", tr).to_string(), - (Some(ty), None) => format!("\n{}\n", ty).to_string(), - (Some(ty), Some(tr)) => { - let updated = ty.clone_for_update(); - tr.predicates().for_each(|p| { - ty.add_predicate(p); - }); - format!("\n{}\n", updated).to_string() - } - }; + let where_clause = merge_where_clause(ty_where_clause, trait_where_clause) + .map_or_else(|| " ".to_string(), |wc| format!("\n{}\n", wc)); let body = match body { Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), None => String::new(), }; - ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{tr_gen_args} for {ty}{ty_gen_args}{where_clause}{{{}}}" , body)) + ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{{{}}}" , body)) } pub fn impl_trait_type(bounds: ast::TypeBoundList) -> ast::ImplTraitType { @@ -922,6 +924,10 @@ pub fn type_param(name: ast::Name, bounds: Option) -> ast::T ast_from_text(&format!("fn f<{name}{bounds}>() {{ }}")) } +pub fn const_param(name: ast::Name, ty: ast::Type) -> ast::ConstParam { + ast_from_text(&format!("fn f() {{ }}")) +} + pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam { ast_from_text(&format!("fn f<{lifetime}>() {{ }}")) } @@ -948,9 +954,7 @@ pub fn turbofish_generic_arg_list( ast_from_text(&format!("const S: T::<{args}> = ();")) } -pub(crate) fn generic_arg_list( - args: impl IntoIterator, -) -> ast::GenericArgList { +pub fn generic_arg_list(args: impl IntoIterator) -> ast::GenericArgList { let args = args.into_iter().join(", "); ast_from_text(&format!("const S: T<{args}> = ();")) } diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index f81dff8840cc..a7e4899fb7ee 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -11,7 +11,7 @@ use rowan::{GreenNodeData, GreenTokenData}; use crate::{ ast::{self, support, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, SyntaxNode}, - NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T, + ted, NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T, }; impl ast::Lifetime { @@ -323,6 +323,10 @@ impl ast::UseTree { pub fn is_simple_path(&self) -> bool { self.use_tree_list().is_none() && self.star_token().is_none() } + + pub fn parent_use_tree_list(&self) -> Option { + self.syntax().parent().and_then(ast::UseTreeList::cast) + } } impl ast::UseTreeList { @@ -340,6 +344,34 @@ impl ast::UseTreeList { .find_map(ast::Comment::cast) .is_some() } + + pub fn comma(&self) -> impl Iterator { + self.syntax() + .children_with_tokens() + .filter_map(|it| it.into_token().filter(|it| it.kind() == T![,])) + } + + /// Remove the unnecessary braces in current `UseTreeList` + pub fn remove_unnecessary_braces(mut self) { + let remove_brace_in_use_tree_list = |u: &ast::UseTreeList| { + let use_tree_count = u.use_trees().count(); + if use_tree_count == 1 { + u.l_curly_token().map(ted::remove); + u.r_curly_token().map(ted::remove); + u.comma().for_each(ted::remove); + } + }; + + // take `use crate::{{{{A}}}}` for example + // the below remove the innermost {}, got `use crate::{{{A}}}` + remove_brace_in_use_tree_list(&self); + + // the below remove othe unnecessary {}, got `use crate::A` + while let Some(parent_use_tree_list) = self.parent_use_tree().parent_use_tree_list() { + remove_brace_in_use_tree_list(&parent_use_tree_list); + self = parent_use_tree_list; + } + } } impl ast::Impl { @@ -585,6 +617,16 @@ impl ast::Item { } } +impl ast::Type { + pub fn generic_arg_list(&self) -> Option { + if let ast::Type::PathType(path_type) = self { + path_type.path()?.segment()?.generic_arg_list() + } else { + None + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum FieldKind { Name(ast::NameRef), diff --git a/crates/test-fixture/Cargo.toml b/crates/test-fixture/Cargo.toml new file mode 100644 index 000000000000..35e39229894f --- /dev/null +++ b/crates/test-fixture/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "test-fixture" +version = "0.0.0" +rust-version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +publish = false + +[dependencies] +hir-expand.workspace = true +test-utils.workspace = true +tt.workspace = true +cfg.workspace = true +base-db.workspace = true +rustc-hash.workspace = true +span.workspace = true +stdx.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/base-db/src/fixture.rs b/crates/test-fixture/src/lib.rs similarity index 87% rename from crates/base-db/src/fixture.rs rename to crates/test-fixture/src/lib.rs index bfdd21555f0a..1a042b2dea20 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/test-fixture/src/lib.rs @@ -1,27 +1,30 @@ //! A set of high-level utility fixture methods to use in tests. -use std::{mem, str::FromStr, sync}; +use std::{mem, ops::Not, str::FromStr, sync}; +use base_db::{ + CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, DependencyKind, + Edition, Env, FileChange, FileSet, LangCrateOrigin, SourceDatabaseExt, SourceRoot, Version, + VfsPath, +}; use cfg::CfgOptions; +use hir_expand::{ + change::Change, + db::ExpandDatabase, + proc_macro::{ + ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacros, + }, +}; use rustc_hash::FxHashMap; +use span::{FileId, FilePosition, FileRange, Span}; use test_utils::{ extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER, }; -use triomphe::Arc; use tt::{Leaf, Subtree, TokenTree}; -use vfs::{file_set::FileSet, VfsPath}; -use crate::{ - input::{CrateName, CrateOrigin, LangCrateOrigin}, - span::SpanData, - Change, CrateDisplayName, CrateGraph, CrateId, Dependency, DependencyKind, Edition, Env, - FileId, FilePosition, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, - ProcMacros, ReleaseChannel, SourceDatabaseExt, SourceRoot, SourceRootId, -}; +pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0); -pub const WORKSPACE: SourceRootId = SourceRootId(0); - -pub trait WithFixture: Default + SourceDatabaseExt + 'static { +pub trait WithFixture: Default + ExpandDatabase + SourceDatabaseExt + 'static { #[track_caller] fn with_single_file(ra_fixture: &str) -> (Self, FileId) { let fixture = ChangeFixture::parse(ra_fixture); @@ -80,6 +83,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); fixture.change.apply(&mut db); + let (file_id, range_or_offset) = fixture .file_position .expect("Could not find file position in fixture. Did you forget to add an `$0`?"); @@ -95,7 +99,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { } } -impl WithFixture for DB {} +impl WithFixture for DB {} pub struct ChangeFixture { pub file_position: Option<(FileId, RangeOrOffset)>, @@ -116,13 +120,11 @@ impl ChangeFixture { ) -> ChangeFixture { let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } = FixtureWithProjectMeta::parse(ra_fixture); - let toolchain = toolchain - .map(|it| { - ReleaseChannel::from_str(&it) - .unwrap_or_else(|| panic!("unknown release channel found: {it}")) - }) - .unwrap_or(ReleaseChannel::Stable); - let mut change = Change::new(); + let toolchain = Some({ + let channel = toolchain.as_deref().unwrap_or("stable"); + Version::parse(&format!("1.76.0-{channel}")).unwrap() + }); + let mut source_change = FileChange::new(); let mut files = Vec::new(); let mut crate_graph = CrateGraph::default(); @@ -187,9 +189,9 @@ impl ChangeFixture { origin, meta.target_data_layout .as_deref() - .map(Arc::from) + .map(From::from) .ok_or_else(|| "target_data_layout unset".into()), - Some(toolchain), + toolchain.clone(), ); let prev = crates.insert(crate_name.clone(), crate_id); assert!(prev.is_none(), "multiple crates with same name: {}", crate_name); @@ -206,7 +208,7 @@ impl ChangeFixture { default_target_data_layout = meta.target_data_layout; } - change.change_file(file_id, Some(Arc::from(text))); + source_change.change_file(file_id, Some(text.into())); let path = VfsPath::new_virtual_path(meta.path); file_set.insert(file_id, path); files.push(file_id); @@ -229,7 +231,7 @@ impl ChangeFixture { default_target_data_layout .map(|it| it.into()) .ok_or_else(|| "target_data_layout unset".into()), - Some(toolchain), + toolchain.clone(), ); } else { for (from, to, prelude) in crate_deps { @@ -261,7 +263,7 @@ impl ChangeFixture { fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string())); roots.push(SourceRoot::new_library(fs)); - change.change_file(core_file, Some(Arc::from(mini_core.source_code()))); + source_change.change_file(core_file, Some(mini_core.source_code().into())); let all_crates = crate_graph.crates_in_topological_order(); @@ -276,7 +278,7 @@ impl ChangeFixture { false, CrateOrigin::Lang(LangCrateOrigin::Core), target_layout.clone(), - Some(toolchain), + toolchain.clone(), ); for krate in all_crates { @@ -306,7 +308,7 @@ impl ChangeFixture { ); roots.push(SourceRoot::new_library(fs)); - change.change_file(proc_lib_file, Some(Arc::from(source))); + source_change.change_file(proc_lib_file, Some(source.into())); let all_crates = crate_graph.crates_in_topological_order(); @@ -321,7 +323,7 @@ impl ChangeFixture { true, CrateOrigin::Local { repo: None, name: None }, target_layout, - Some(toolchain), + toolchain, ); proc_macros.insert(proc_macros_crate, Ok(proc_macro)); @@ -344,11 +346,17 @@ impl ChangeFixture { SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)), }; roots.push(root); - change.set_roots(roots); - change.set_crate_graph(crate_graph); - change.set_proc_macros(proc_macros); + source_change.set_roots(roots); + source_change.set_crate_graph(crate_graph); - ChangeFixture { file_position, files, change } + ChangeFixture { + file_position, + files, + change: Change { + source_change, + proc_macros: proc_macros.is_empty().not().then(|| proc_macros), + }, + } } } @@ -364,7 +372,7 @@ pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream { .into(), ProcMacro { name: "identity".into(), - kind: crate::ProcMacroKind::Attr, + kind: ProcMacroKind::Attr, expander: sync::Arc::new(IdentityProcMacroExpander), }, ), @@ -378,7 +386,7 @@ pub fn derive_identity(item: TokenStream) -> TokenStream { .into(), ProcMacro { name: "DeriveIdentity".into(), - kind: crate::ProcMacroKind::CustomDerive, + kind: ProcMacroKind::CustomDerive, expander: sync::Arc::new(IdentityProcMacroExpander), }, ), @@ -392,7 +400,7 @@ pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream { .into(), ProcMacro { name: "input_replace".into(), - kind: crate::ProcMacroKind::Attr, + kind: ProcMacroKind::Attr, expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander), }, ), @@ -406,7 +414,7 @@ pub fn mirror(input: TokenStream) -> TokenStream { .into(), ProcMacro { name: "mirror".into(), - kind: crate::ProcMacroKind::FuncLike, + kind: ProcMacroKind::FuncLike, expander: sync::Arc::new(MirrorProcMacroExpander), }, ), @@ -420,7 +428,7 @@ pub fn shorten(input: TokenStream) -> TokenStream { .into(), ProcMacro { name: "shorten".into(), - kind: crate::ProcMacroKind::FuncLike, + kind: ProcMacroKind::FuncLike, expander: sync::Arc::new(ShortenProcMacroExpander), }, ), @@ -539,13 +547,13 @@ struct IdentityProcMacroExpander; impl ProcMacroExpander for IdentityProcMacroExpander { fn expand( &self, - subtree: &Subtree, - _: Option<&Subtree>, + subtree: &Subtree, + _: Option<&Subtree>, _: &Env, - _: SpanData, - _: SpanData, - _: SpanData, - ) -> Result, ProcMacroExpansionError> { + _: Span, + _: Span, + _: Span, + ) -> Result, ProcMacroExpansionError> { Ok(subtree.clone()) } } @@ -556,13 +564,13 @@ struct AttributeInputReplaceProcMacroExpander; impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { fn expand( &self, - _: &Subtree, - attrs: Option<&Subtree>, + _: &Subtree, + attrs: Option<&Subtree>, _: &Env, - _: SpanData, - _: SpanData, - _: SpanData, - ) -> Result, ProcMacroExpansionError> { + _: Span, + _: Span, + _: Span, + ) -> Result, ProcMacroExpansionError> { attrs .cloned() .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into())) @@ -574,14 +582,14 @@ struct MirrorProcMacroExpander; impl ProcMacroExpander for MirrorProcMacroExpander { fn expand( &self, - input: &Subtree, - _: Option<&Subtree>, + input: &Subtree, + _: Option<&Subtree>, _: &Env, - _: SpanData, - _: SpanData, - _: SpanData, - ) -> Result, ProcMacroExpansionError> { - fn traverse(input: &Subtree) -> Subtree { + _: Span, + _: Span, + _: Span, + ) -> Result, ProcMacroExpansionError> { + fn traverse(input: &Subtree) -> Subtree { let mut token_trees = vec![]; for tt in input.token_trees.iter().rev() { let tt = match tt { @@ -604,16 +612,16 @@ struct ShortenProcMacroExpander; impl ProcMacroExpander for ShortenProcMacroExpander { fn expand( &self, - input: &Subtree, - _: Option<&Subtree>, + input: &Subtree, + _: Option<&Subtree>, _: &Env, - _: SpanData, - _: SpanData, - _: SpanData, - ) -> Result, ProcMacroExpansionError> { + _: Span, + _: Span, + _: Span, + ) -> Result, ProcMacroExpansionError> { return Ok(traverse(input)); - fn traverse(input: &Subtree) -> Subtree { + fn traverse(input: &Subtree) -> Subtree { let token_trees = input .token_trees .iter() @@ -625,7 +633,7 @@ impl ProcMacroExpander for ShortenProcMacroExpander { Subtree { delimiter: input.delimiter, token_trees } } - fn modify_leaf(leaf: &Leaf) -> Leaf { + fn modify_leaf(leaf: &Leaf) -> Leaf { let mut leaf = leaf.clone(); match &mut leaf { Leaf::Literal(it) => { diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 438b599ffaae..56067d834178 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -15,7 +15,10 @@ doctest = false # Avoid adding deps here, this crate is widely used in tests it should compile fast! dissimilar = "1.0.7" text-size.workspace = true -rustc-hash = "1.1.0" +rustc-hash.workspace = true stdx.workspace = true profile.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index f766747d707c..1f3136404c61 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -1381,6 +1381,7 @@ mod macros { // region:assert #[macro_export] #[rustc_builtin_macro] + #[allow_internal_unstable(core_panic, edition_panic, generic_assert_internals)] macro_rules! assert { ($($arg:tt)*) => { /* compiler built-in */ @@ -1389,6 +1390,7 @@ mod macros { // endregion:assert // region:fmt + #[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)] #[macro_export] #[rustc_builtin_macro] macro_rules! const_format_args { @@ -1396,6 +1398,7 @@ mod macros { ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; } + #[allow_internal_unstable(fmt_internals)] #[macro_export] #[rustc_builtin_macro] macro_rules! format_args { @@ -1403,6 +1406,7 @@ mod macros { ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; } + #[allow_internal_unstable(fmt_internals)] #[macro_export] #[rustc_builtin_macro] macro_rules! format_args_nl { diff --git a/crates/text-edit/Cargo.toml b/crates/text-edit/Cargo.toml index 4620cc72d0a2..f745674794c9 100644 --- a/crates/text-edit/Cargo.toml +++ b/crates/text-edit/Cargo.toml @@ -14,3 +14,6 @@ doctest = false [dependencies] itertools.workspace = true text-size.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/toolchain/Cargo.toml b/crates/toolchain/Cargo.toml index a283f9a88421..f9b120772f03 100644 --- a/crates/toolchain/Cargo.toml +++ b/crates/toolchain/Cargo.toml @@ -13,3 +13,6 @@ doctest = false [dependencies] home = "0.5.4" + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/tt/Cargo.toml b/crates/tt/Cargo.toml index 57222449790e..77683fd48afb 100644 --- a/crates/tt/Cargo.toml +++ b/crates/tt/Cargo.toml @@ -16,3 +16,9 @@ smol_str.workspace = true text-size.workspace = true stdx.workspace = true + +# FIXME: Remove this dependency once the `Span` trait is gone (that is once Span::DUMMY has been removed) +span.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index 481d575403ac..b3b0eeda75af 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs @@ -11,46 +11,10 @@ use stdx::impl_from; pub use smol_str::SmolStr; pub use text_size::{TextRange, TextSize}; -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct SpanData { - /// The text range of this span, relative to the anchor. - /// We need the anchor for incrementality, as storing absolute ranges will require - /// recomputation on every change in a file at all times. - pub range: TextRange, - pub anchor: Anchor, - /// The syntax context of the span. - pub ctx: Ctx, -} +pub trait Span: std::fmt::Debug + Copy + Sized + Eq {} -impl Span for SpanData { - #[allow(deprecated)] - const DUMMY: Self = SpanData { - range: TextRange::empty(TextSize::new(0)), - anchor: Anchor::DUMMY, - ctx: Ctx::DUMMY, - }; -} - -pub trait Span: std::fmt::Debug + Copy + Sized + Eq { - // FIXME: Should not exist. Dummy spans will always be wrong if they leak somewhere. Instead, - // the call site or def site spans should be used in relevant places, its just that we don't - // expose those everywhere in the yet. - const DUMMY: Self; -} - -// FIXME: Should not exist -pub trait SpanAnchor: - std::fmt::Debug + Copy + Sized + Eq + Copy + fmt::Debug + std::hash::Hash -{ - #[deprecated(note = "this should not exist")] - const DUMMY: Self; -} - -// FIXME: Should not exist -pub trait SyntaxContext: std::fmt::Debug + Copy + Sized + Eq { - #[deprecated(note = "this should not exist")] - const DUMMY: Self; -} +impl Span for span::SpanData where span::SpanData: std::fmt::Debug + Copy + Sized + Eq +{} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TokenTree { @@ -66,15 +30,7 @@ impl TokenTree { }) } - pub fn subtree_or_wrap(self) -> Subtree { - match self { - TokenTree::Leaf(_) => { - Subtree { delimiter: Delimiter::DUMMY_INVISIBLE, token_trees: vec![self] } - } - TokenTree::Subtree(s) => s, - } - } - pub fn subtree_or_wrap2(self, span: DelimSpan) -> Subtree { + pub fn subtree_or_wrap(self, span: DelimSpan) -> Subtree { match self { TokenTree::Leaf(_) => Subtree { delimiter: Delimiter::invisible_delim_spanned(span), @@ -83,6 +39,13 @@ impl TokenTree { TokenTree::Subtree(s) => s, } } + + pub fn first_span(&self) -> S { + match self { + TokenTree::Leaf(l) => *l.span(), + TokenTree::Subtree(s) => s.delimiter.open, + } + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -134,11 +97,6 @@ pub struct DelimSpan { pub close: S, } -impl DelimSpan { - // FIXME should not exist - pub const DUMMY: Self = Self { open: S::DUMMY, close: S::DUMMY }; -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Delimiter { pub open: S, @@ -147,15 +105,6 @@ pub struct Delimiter { } impl Delimiter { - // FIXME should not exist - pub const DUMMY_INVISIBLE: Self = - Self { open: S::DUMMY, close: S::DUMMY, kind: DelimiterKind::Invisible }; - - // FIXME should not exist - pub const fn dummy_invisible() -> Self { - Self::DUMMY_INVISIBLE - } - pub const fn invisible_spanned(span: S) -> Self { Delimiter { open: span, close: span, kind: DelimiterKind::Invisible } } diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index fe6cb0a2c3fd..a6d5027c3a60 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml @@ -20,3 +20,6 @@ notify = "6.1.1" stdx.workspace = true vfs.workspace = true paths.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml index 11409f2eb819..c88f34665598 100644 --- a/crates/vfs/Cargo.toml +++ b/crates/vfs/Cargo.toml @@ -12,10 +12,13 @@ rust-version.workspace = true doctest = false [dependencies] -rustc-hash = "1.1.0" +rustc-hash.workspace = true fst = "0.4.7" indexmap.workspace = true nohash-hasher.workspace = true paths.workspace = true stdx.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index b7d585cafb35..4303a800a04a 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md @@ -134,29 +134,29 @@ This is to enable parallel parsing of all files. **Architecture Invariant:** Syntax trees are by design incomplete and do not enforce well-formedness. If an AST method returns an `Option`, it *can* be `None` at runtime, even if this is forbidden by the grammar. -### `crates/base_db` +### `crates/base-db` We use the [salsa](https://github.com/salsa-rs/salsa) crate for incremental and on-demand computation. Roughly, you can think of salsa as a key-value store, but it can also compute derived values using specified functions. -The `base_db` crate provides basic infrastructure for interacting with salsa. +The `base-db` crate provides basic infrastructure for interacting with salsa. Crucially, it defines most of the "input" queries: facts supplied by the client of the analyzer. Reading the docs of the `base_db::input` module should be useful: everything else is strictly derived from those inputs. **Architecture Invariant:** particularities of the build system are *not* the part of the ground state. -In particular, `base_db` knows nothing about cargo. +In particular, `base-db` knows nothing about cargo. For example, `cfg` flags are a part of `base_db`, but `feature`s are not. A `foo` feature is a Cargo-level concept, which is lowered by Cargo to `--cfg feature=foo` argument on the command line. The `CrateGraph` structure is used to represent the dependencies between the crates abstractly. -**Architecture Invariant:** `base_db` doesn't know about file system and file paths. +**Architecture Invariant:** `base-db` doesn't know about file system and file paths. Files are represented with opaque `FileId`, there's no operation to get an `std::path::Path` out of the `FileId`. -### `crates/hir_expand`, `crates/hir_def`, `crates/hir_ty` +### `crates/hir-expand`, `crates/hir-def`, `crates/hir_ty` These crates are the *brain* of rust-analyzer. This is the compiler part of the IDE. -`hir_xxx` crates have a strong [ECS](https://en.wikipedia.org/wiki/Entity_component_system) flavor, in that they work with raw ids and directly query the database. +`hir-xxx` crates have a strong [ECS](https://en.wikipedia.org/wiki/Entity_component_system) flavor, in that they work with raw ids and directly query the database. There's little abstraction here. These crates integrate deeply with salsa and chalk. @@ -186,7 +186,7 @@ If you think about "using rust-analyzer as a library", `hir` crate is most likel It wraps ECS-style internal API into a more OO-flavored API (with an extra `db` argument for each call). **Architecture Invariant:** `hir` provides a static, fully resolved view of the code. -While internal `hir_*` crates _compute_ things, `hir`, from the outside, looks like an inert data structure. +While internal `hir-*` crates _compute_ things, `hir`, from the outside, looks like an inert data structure. `hir` also handles the delicate task of going from syntax to the corresponding `hir`. Remember that the mapping here is one-to-many. @@ -200,7 +200,7 @@ Then we look for our node in the set of children. This is the heart of many IDE features, like goto definition, which start with figuring out the hir node at the cursor. This is some kind of (yet unnamed) uber-IDE pattern, as it is present in Roslyn and Kotlin as well. -### `crates/ide` +### `crates/ide`, `crates/ide-db`, `crates/ide-assists`, `crates/ide-completion`, `crates/ide-diagnostics`, `crates/ide-ssr` The `ide` crate builds on top of `hir` semantic model to provide high-level IDE features like completion or goto definition. It is an **API Boundary**. @@ -217,8 +217,8 @@ Shout outs to LSP developers for popularizing the idea that "UI" is a good place `AnalysisHost` is a state to which you can transactionally `apply_change`. `Analysis` is an immutable snapshot of the state. -Internally, `ide` is split across several crates. `ide_assists`, `ide_completion` and `ide_ssr` implement large isolated features. -`ide_db` implements common IDE functionality (notably, reference search is implemented here). +Internally, `ide` is split across several crates. `ide-assists`, `ide-completion`, `ide-diagnostics` and `ide-ssr` implement large isolated features. +`ide-db` implements common IDE functionality (notably, reference search is implemented here). The `ide` contains a public API/façade, as well as implementation for a plethora of smaller features. **Architecture Invariant:** `ide` crate strives to provide a _perfect_ API. @@ -251,14 +251,14 @@ This is a tricky business. **Architecture Invariant:** `rust-analyzer` should be partially available even when the build is broken. Reloading process should not prevent IDE features from working. -### `crates/toolchain`, `crates/project_model`, `crates/flycheck` +### `crates/toolchain`, `crates/project-model`, `crates/flycheck` These crates deal with invoking `cargo` to learn about project structure and get compiler errors for the "check on save" feature. -They use `crates/path` heavily instead of `std::path`. +They use `crates/paths` heavily instead of `std::path`. A single `rust-analyzer` process can serve many projects, so it is important that server's current directory does not leak. -### `crates/mbe`, `crates/tt`, `crates/proc_macro_api`, `crates/proc_macro_srv` +### `crates/mbe`, `crates/tt`, `crates/proc-macro-api`, `crates/proc-macro-srv`, `crates/proc-macro-srv-cli` These crates implement macros as token tree -> token tree transforms. They are independent from the rest of the code. @@ -268,8 +268,8 @@ They are independent from the rest of the code. And it also handles the actual parsing and expansion of declarative macro (a-la "Macros By Example" or mbe). For proc macros, the client-server model are used. -We start a separate process (`proc_macro_srv`) which loads and runs the proc-macros for us. -And the client (`proc_macro_api`) provides an interface to talk to that server separately. +We start a separate process (`proc-macro-srv-cli`) which loads and runs the proc-macros for us. +And the client (`proc-macro-api`) provides an interface to talk to that server separately. And then token trees are passed from client, and the server will load the corresponding dynamic library (which built by `cargo`). And due to the fact the api for getting result from proc macro are always unstable in `rustc`, @@ -283,7 +283,7 @@ And they may be non-deterministic which conflict how `salsa` works, so special a This crate is responsible for parsing, evaluation and general definition of `cfg` attributes. -### `crates/vfs`, `crates/vfs-notify` +### `crates/vfs`, `crates/vfs-notify`, `crates/paths` These crates implement a virtual file system. They provide consistent snapshots of the underlying file system and insulate messy OS paths. @@ -301,6 +301,25 @@ as copies of unstable std items we would like to make use of already, like `std: This crate contains utilities for CPU and memory profiling. +### `crates/intern` + +This crate contains infrastructure for globally interning things via `Arc`. + +### `crates/load-cargo` + +This crate exposes several utilities for loading projects, used by the main `rust-analyzer` crate +and other downstream consumers. + +### `crates/rustc-dependencies` + +This crate wraps the `rustc_*` crates rust-analyzer relies on and conditionally points them to +mirrored crates-io releases such that rust-analyzer keeps building on stable. + +### `crates/span` + +This crate exposes types and functions related to rust-analyzer's span for macros. + +A span is effectively a text range relative to some item in a file with a given `SyntaxContext` (hygiene). ## Cross-Cutting Concerns diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index b66c9c943a16..3251dd752682 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/error_is_shown_in_downstream_crates.rs:10:14 + | +LL | take_foo(()); + | -------- ^^ label + | | + | required by a bound introduced by this call + | + = help: the trait `Foo` is not implemented for `()` + = note: Note +note: required by a bound in `take_foo` + --> $DIR/error_is_shown_in_downstream_crates.rs:7:21 + | +LL | fn take_foo(_: impl Foo) {} + | ^^^ required by this bound in `take_foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 0e9c3d95336efb6dd85f6b97235f58a7cf040bfc Mon Sep 17 00:00:00 2001 From: martha Date: Fri, 5 Jan 2024 03:34:21 +0100 Subject: [PATCH 097/257] Fix #119551: Rewrite Iterator::position default impl, storing the accumulating value outside of the fold in an attempt to improve code generation Squashed with: Add inheriting overflow checks back --- library/core/src/iter/traits/iterator.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 8e2c887a161e..d94a508b5b28 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -3094,16 +3094,23 @@ pub trait Iterator { P: FnMut(Self::Item) -> bool, { #[inline] - fn check( - mut predicate: impl FnMut(T) -> bool, - ) -> impl FnMut(usize, T) -> ControlFlow { + fn check<'a, T>( + mut predicate: impl FnMut(T) -> bool + 'a, + acc: &'a mut usize, + ) -> impl FnMut((), T) -> ControlFlow + 'a { #[rustc_inherit_overflow_checks] - move |i, x| { - if predicate(x) { ControlFlow::Break(i) } else { ControlFlow::Continue(i + 1) } + move |_, x| { + if predicate(x) { + ControlFlow::Break(*acc) + } else { + *acc += 1; + ControlFlow::Continue(()) + } } } - self.try_fold(0, check(predicate)).break_value() + let mut acc = 0; + self.try_fold((), check(predicate, &mut acc)).break_value() } /// Searches for an element in an iterator from the right, returning its From deecbd80cd74454e086fa28a9066381c306e3f52 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 26 Dec 2023 16:30:31 +0300 Subject: [PATCH 098/257] library: Add `allow(unused_assignments)` to custom MIR doctest The lint is not yet reported due to span issues, but will be reported later. --- library/core/src/intrinsics/mir.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index c6401ec1e333..334e32b26b18 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -65,6 +65,7 @@ //! ```rust //! #![feature(core_intrinsics, custom_mir)] //! #![allow(internal_features)] +//! #![allow(unused_assignments)] //! //! use core::intrinsics::mir::*; //! From 205fb75cfa3339fcbac70e8da9c381060a8940cc Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 5 Jan 2024 17:11:25 +0300 Subject: [PATCH 099/257] rustc_span: Remove `fn fresh_expansion` In the past it did create a fresh expansion, but now, after surviving a number of refactorings, it does not. Now it's just a thin wrapper around `apply_mark`. --- compiler/rustc_span/src/hygiene.rs | 17 +---------------- .../passes/lint/check_code_block_syntax.rs | 4 ++-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index b717229b68df..fec05e8e0b38 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -850,21 +850,6 @@ impl fmt::Debug for SyntaxContext { } impl Span { - /// Creates a fresh expansion with given properties. - /// Expansions are normally created by macros, but in some cases expansions are created for - /// other compiler-generated code to set per-span properties like allowed unstable features. - /// The returned span belongs to the created expansion and has the new properties, - /// but its location is inherited from the current span. - pub fn fresh_expansion(self, expn_id: LocalExpnId) -> Span { - HygieneData::with(|data| { - self.with_ctxt(data.apply_mark( - self.ctxt(), - expn_id.to_expn_id(), - Transparency::Transparent, - )) - }) - } - /// Reuses the span but adds information like the kind of the desugaring and features that are /// allowed inside this span. pub fn mark_with_reason( @@ -879,7 +864,7 @@ impl Span { ..ExpnData::default(ExpnKind::Desugaring(reason), self, edition, None, None) }; let expn_id = LocalExpnId::fresh(expn_data, ctx); - self.fresh_expansion(expn_id) + self.apply_mark(expn_id.to_expn_id(), Transparency::Transparent) } } diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index 53c7f0f6e15d..782938f10943 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -8,7 +8,7 @@ use rustc_errors::{ use rustc_parse::parse_stream_from_source_str; use rustc_resolve::rustdoc::source_span_for_markdown_range; use rustc_session::parse::ParseSess; -use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId}; +use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId, Transparency}; use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{FileName, InnerSpan, DUMMY_SP}; @@ -50,7 +50,7 @@ fn check_rust_syntax( let expn_data = ExpnData::default(ExpnKind::AstPass(AstPass::TestHarness), DUMMY_SP, edition, None, None); let expn_id = cx.tcx.with_stable_hashing_context(|hcx| LocalExpnId::fresh(expn_data, hcx)); - let span = DUMMY_SP.fresh_expansion(expn_id); + let span = DUMMY_SP.apply_mark(expn_id.to_expn_id(), Transparency::Transparent); let is_empty = rustc_driver::catch_fatal_errors(|| { parse_stream_from_source_str( From c586fe40f1d1b136604250e7c0bd5d1e8bc41f5f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 5 Jan 2024 17:25:31 +0300 Subject: [PATCH 100/257] macro_rules: Add more tests for using `tt` in addition to `ident` Generally, `tt` and `ident` should behave identically, modulo the latter accepting only a subset of token trees. --- .../anon-params-edition-hygiene.rs | 5 +-- .../anon-params-edition-hygiene.stderr | 23 +++++++++++++ .../auxiliary/anon-params-edition-hygiene.rs | 13 +++++-- .../auxiliary/edition-kw-macro-2015.rs | 5 +++ .../auxiliary/edition-kw-macro-2018.rs | 5 +++ .../edition-keywords-2015-2015-parsing.rs | 2 ++ .../ui/editions/edition-keywords-2015-2015.rs | 2 ++ .../edition-keywords-2015-2018-parsing.rs | 2 ++ .../ui/editions/edition-keywords-2015-2018.rs | 2 ++ .../edition-keywords-2018-2015-parsing.rs | 4 ++- .../edition-keywords-2018-2015-parsing.stderr | 14 +++++--- .../ui/editions/edition-keywords-2018-2015.rs | 2 ++ .../edition-keywords-2018-2018-parsing.rs | 15 +++++++- .../edition-keywords-2018-2018-parsing.stderr | 34 ++++++++++++++----- .../ui/editions/edition-keywords-2018-2018.rs | 2 ++ tests/ui/lint/wide_pointer_comparisons.rs | 9 +++++ tests/ui/lint/wide_pointer_comparisons.stderr | 17 ++++++++-- .../method-on-ambiguous-numeric-type.rs | 7 ++++ .../method-on-ambiguous-numeric-type.stderr | 23 +++++++++---- 19 files changed, 159 insertions(+), 27 deletions(-) create mode 100644 tests/ui/anon-params/anon-params-edition-hygiene.stderr diff --git a/tests/ui/anon-params/anon-params-edition-hygiene.rs b/tests/ui/anon-params/anon-params-edition-hygiene.rs index 6936205f8b96..0b69081d4eda 100644 --- a/tests/ui/anon-params/anon-params-edition-hygiene.rs +++ b/tests/ui/anon-params/anon-params-edition-hygiene.rs @@ -1,4 +1,3 @@ -// check-pass // edition:2018 // aux-build:anon-params-edition-hygiene.rs @@ -8,6 +7,8 @@ #[macro_use] extern crate anon_params_edition_hygiene; -generate_trait_2015!(u8); +generate_trait_2015_ident!(u8); +// FIXME: Edition hygiene doesn't work correctly with `tt`s in this case. +generate_trait_2015_tt!(u8); //~ ERROR expected one of `:`, `@`, or `|`, found `)` fn main() {} diff --git a/tests/ui/anon-params/anon-params-edition-hygiene.stderr b/tests/ui/anon-params/anon-params-edition-hygiene.stderr new file mode 100644 index 000000000000..373d7c6aebb5 --- /dev/null +++ b/tests/ui/anon-params/anon-params-edition-hygiene.stderr @@ -0,0 +1,23 @@ +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/anon-params-edition-hygiene.rs:12:1 + | +LL | generate_trait_2015_tt!(u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected one of `:`, `@`, or `|` + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) + = note: this error originates in the macro `generate_trait_2015_tt` (in Nightly builds, run with -Z macro-backtrace for more info) +help: if this is a `self` type, give it a parameter name + | +LL | generate_trait_2015_tt!(self: u8); + | +++++ +help: if this is a parameter name, give it a type + | +LL | generate_trait_2015_tt!(u8: TypeName); + | ++++++++++ +help: if this is a type, explicitly ignore the parameter name + | +LL | generate_trait_2015_tt!(_: u8); + | ++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs b/tests/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs index aa4221becc24..283656552931 100644 --- a/tests/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs +++ b/tests/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs @@ -1,9 +1,18 @@ // edition:2015 #[macro_export] -macro_rules! generate_trait_2015 { +macro_rules! generate_trait_2015_ident { ($Type: ident) => { - trait Trait { + trait Trait1 { + fn method($Type) {} + } + }; +} + +#[macro_export] +macro_rules! generate_trait_2015_tt { + ($Type: tt) => { + trait Trait2 { fn method($Type) {} } }; diff --git a/tests/ui/editions/auxiliary/edition-kw-macro-2015.rs b/tests/ui/editions/auxiliary/edition-kw-macro-2015.rs index 7cfd128f2bfe..a4a2b156e139 100644 --- a/tests/ui/editions/auxiliary/edition-kw-macro-2015.rs +++ b/tests/ui/editions/auxiliary/edition-kw-macro-2015.rs @@ -26,3 +26,8 @@ macro_rules! consumes_async_raw { macro_rules! passes_ident { ($i: ident) => ($i) } + +#[macro_export] +macro_rules! passes_tt { + ($i: tt) => ($i) +} diff --git a/tests/ui/editions/auxiliary/edition-kw-macro-2018.rs b/tests/ui/editions/auxiliary/edition-kw-macro-2018.rs index d07c0218db3f..02db38103d2a 100644 --- a/tests/ui/editions/auxiliary/edition-kw-macro-2018.rs +++ b/tests/ui/editions/auxiliary/edition-kw-macro-2018.rs @@ -26,3 +26,8 @@ macro_rules! consumes_async_raw { macro_rules! passes_ident { ($i: ident) => ($i) } + +#[macro_export] +macro_rules! passes_tt { + ($i: tt) => ($i) +} diff --git a/tests/ui/editions/edition-keywords-2015-2015-parsing.rs b/tests/ui/editions/edition-keywords-2015-2015-parsing.rs index d1752a7ec71d..3574bc815155 100644 --- a/tests/ui/editions/edition-keywords-2015-2015-parsing.rs +++ b/tests/ui/editions/edition-keywords-2015-2015-parsing.rs @@ -19,6 +19,8 @@ pub fn check_async() { if passes_ident!(async) == 1 {} // OK if passes_ident!(r#async) == 1 {} // OK + if passes_tt!(async) == 1 {} // OK + if passes_tt!(r#async) == 1 {} // OK module::async(); // OK module::r#async(); // OK } diff --git a/tests/ui/editions/edition-keywords-2015-2015.rs b/tests/ui/editions/edition-keywords-2015-2015.rs index 943d203b806f..77a2cb2e6dea 100644 --- a/tests/ui/editions/edition-keywords-2015-2015.rs +++ b/tests/ui/editions/edition-keywords-2015-2015.rs @@ -20,6 +20,8 @@ pub fn check_async() { if passes_ident!(async) == 1 {} // OK if passes_ident!(r#async) == 1 {} // OK + if passes_tt!(async) == 1 {} // OK + if passes_tt!(r#async) == 1 {} // OK one_async::async(); // OK one_async::r#async(); // OK two_async::async(); // OK diff --git a/tests/ui/editions/edition-keywords-2015-2018-parsing.rs b/tests/ui/editions/edition-keywords-2015-2018-parsing.rs index 44455f43856c..49f8562a6b19 100644 --- a/tests/ui/editions/edition-keywords-2015-2018-parsing.rs +++ b/tests/ui/editions/edition-keywords-2015-2018-parsing.rs @@ -19,6 +19,8 @@ pub fn check_async() { if passes_ident!(async) == 1 {} // OK if passes_ident!(r#async) == 1 {} // OK + if passes_tt!(async) == 1 {} // OK + if passes_tt!(r#async) == 1 {} // OK module::async(); // OK module::r#async(); // OK } diff --git a/tests/ui/editions/edition-keywords-2015-2018.rs b/tests/ui/editions/edition-keywords-2015-2018.rs index 8c3397c951db..a431a06bd104 100644 --- a/tests/ui/editions/edition-keywords-2015-2018.rs +++ b/tests/ui/editions/edition-keywords-2015-2018.rs @@ -20,6 +20,8 @@ pub fn check_async() { if passes_ident!(async) == 1 {} // OK if passes_ident!(r#async) == 1 {} // OK + if passes_tt!(async) == 1 {} // OK + if passes_tt!(r#async) == 1 {} // OK // one_async::async(); // ERROR, unresolved name // one_async::r#async(); // ERROR, unresolved name two_async::async(); // OK diff --git a/tests/ui/editions/edition-keywords-2018-2015-parsing.rs b/tests/ui/editions/edition-keywords-2018-2015-parsing.rs index d5ed9fb9a285..8472430361fb 100644 --- a/tests/ui/editions/edition-keywords-2018-2015-parsing.rs +++ b/tests/ui/editions/edition-keywords-2018-2015-parsing.rs @@ -21,8 +21,10 @@ pub fn check_async() { r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` r#async = consumes_async_raw!(r#async); // OK - if passes_ident!(async) == 1 {} + if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved if passes_ident!(r#async) == 1 {} // OK + if passes_tt!(async) == 1 {} //~ ERROR macro expansion ends with an incomplete expression + if passes_tt!(r#async) == 1 {} // OK module::async(); //~ ERROR expected identifier, found keyword `async` module::r#async(); // OK diff --git a/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr b/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr index 1a4a94e97332..42db75f66597 100644 --- a/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr +++ b/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr @@ -10,7 +10,7 @@ LL | let mut r#async = 1; | ++ error: expected identifier, found keyword `async` - --> $DIR/edition-keywords-2018-2015-parsing.rs:26:13 + --> $DIR/edition-keywords-2018-2015-parsing.rs:28:13 | LL | module::async(); | ^^^^^ expected identifier, found keyword @@ -52,17 +52,23 @@ LL | ($i: ident) => ($i) | ::: $DIR/edition-keywords-2018-2015-parsing.rs:24:8 | -LL | if passes_ident!(async) == 1 {} +LL | if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved | -------------------- in this macro invocation +error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` + --> $DIR/edition-keywords-2018-2015-parsing.rs:26:24 + | +LL | if passes_tt!(async) == 1 {} + | ^ expected one of `move`, `|`, or `||` + error[E0308]: mismatched types - --> $DIR/edition-keywords-2018-2015-parsing.rs:29:33 + --> $DIR/edition-keywords-2018-2015-parsing.rs:31:33 | LL | let _recovery_witness: () = 0; | -- ^ expected `()`, found integer | | | expected due to this -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/editions/edition-keywords-2018-2015.rs b/tests/ui/editions/edition-keywords-2018-2015.rs index 2cb2dfb18a02..4a02f8671721 100644 --- a/tests/ui/editions/edition-keywords-2018-2015.rs +++ b/tests/ui/editions/edition-keywords-2018-2015.rs @@ -18,6 +18,8 @@ pub fn check_async() { // if passes_ident!(async) == 1 {} // ERROR, reserved if passes_ident!(r#async) == 1 {} // OK + // if passes_tt!(async) == 1 {} // ERROR, reserved + if passes_tt!(r#async) == 1 {} // OK // one_async::async(); // ERROR, reserved one_async::r#async(); // OK // two_async::async(); // ERROR, reserved diff --git a/tests/ui/editions/edition-keywords-2018-2018-parsing.rs b/tests/ui/editions/edition-keywords-2018-2018-parsing.rs index 044ab249f2c2..c0d8927d0597 100644 --- a/tests/ui/editions/edition-keywords-2018-2018-parsing.rs +++ b/tests/ui/editions/edition-keywords-2018-2018-parsing.rs @@ -12,6 +12,13 @@ mod module { pub fn r#async() {} } +macro_rules! local_passes_ident { + ($i: ident) => ($i) //~ ERROR macro expansion ends with an incomplete expression +} +macro_rules! local_passes_tt { + ($i: tt) => ($i) //~ ERROR macro expansion ends with an incomplete expression +} + pub fn check_async() { let mut async = 1; //~ ERROR expected identifier, found keyword `async` let mut r#async = 1; // OK @@ -21,8 +28,14 @@ pub fn check_async() { r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` r#async = consumes_async_raw!(r#async); // OK - if passes_ident!(async) == 1 {} + if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved if passes_ident!(r#async) == 1 {} // OK + if passes_tt!(async) == 1 {} //~ ERROR macro expansion ends with an incomplete expression + if passes_tt!(r#async) == 1 {} // OK + if local_passes_ident!(async) == 1 {} // Error reported above in the macro + if local_passes_ident!(r#async) == 1 {} // OK + if local_passes_tt!(async) == 1 {} // Error reported above in the macro + if local_passes_tt!(r#async) == 1 {} // OK module::async(); //~ ERROR expected identifier, found keyword `async` module::r#async(); // OK diff --git a/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr b/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr index 19eb7ac98239..6f08cff433b1 100644 --- a/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr +++ b/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr @@ -1,5 +1,5 @@ error: expected identifier, found keyword `async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:16:13 + --> $DIR/edition-keywords-2018-2018-parsing.rs:23:13 | LL | let mut async = 1; | ^^^^^ expected identifier, found keyword @@ -10,7 +10,7 @@ LL | let mut r#async = 1; | ++ error: expected identifier, found keyword `async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:26:13 + --> $DIR/edition-keywords-2018-2018-parsing.rs:39:13 | LL | module::async(); | ^^^^^ expected identifier, found keyword @@ -21,7 +21,7 @@ LL | module::r#async(); | ++ error: no rules expected the token `r#async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:20:31 + --> $DIR/edition-keywords-2018-2018-parsing.rs:27:31 | LL | r#async = consumes_async!(r#async); | ^^^^^^^ no rules expected this token in macro call @@ -33,7 +33,7 @@ LL | (async) => (1) | ^^^^^ error: no rules expected the token `async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:21:35 + --> $DIR/edition-keywords-2018-2018-parsing.rs:28:35 | LL | r#async = consumes_async_raw!(async); | ^^^^^ no rules expected this token in macro call @@ -50,19 +50,37 @@ error: macro expansion ends with an incomplete expression: expected one of `move LL | ($i: ident) => ($i) | ^ expected one of `move`, `|`, or `||` | - ::: $DIR/edition-keywords-2018-2018-parsing.rs:24:8 + ::: $DIR/edition-keywords-2018-2018-parsing.rs:31:8 | -LL | if passes_ident!(async) == 1 {} +LL | if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved | -------------------- in this macro invocation +error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` + --> $DIR/edition-keywords-2018-2018-parsing.rs:33:24 + | +LL | if passes_tt!(async) == 1 {} + | ^ expected one of `move`, `|`, or `||` + +error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` + --> $DIR/edition-keywords-2018-2018-parsing.rs:16:23 + | +LL | ($i: ident) => ($i) + | ^ expected one of `move`, `|`, or `||` + +error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` + --> $DIR/edition-keywords-2018-2018-parsing.rs:19:20 + | +LL | ($i: tt) => ($i) + | ^ expected one of `move`, `|`, or `||` + error[E0308]: mismatched types - --> $DIR/edition-keywords-2018-2018-parsing.rs:29:33 + --> $DIR/edition-keywords-2018-2018-parsing.rs:42:33 | LL | let _recovery_witness: () = 0; | -- ^ expected `()`, found integer | | | expected due to this -error: aborting due to 6 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/editions/edition-keywords-2018-2018.rs b/tests/ui/editions/edition-keywords-2018-2018.rs index 5043440aa167..e72943261375 100644 --- a/tests/ui/editions/edition-keywords-2018-2018.rs +++ b/tests/ui/editions/edition-keywords-2018-2018.rs @@ -18,6 +18,8 @@ pub fn check_async() { // if passes_ident!(async) == 1 {} // ERROR, reserved if passes_ident!(r#async) == 1 {} // OK + // if passes_tt!(async) == 1 {} // ERROR, reserved + if passes_tt!(r#async) == 1 {} // OK // one_async::async(); // ERROR, reserved // one_async::r#async(); // ERROR, unresolved name // two_async::async(); // ERROR, reserved diff --git a/tests/ui/lint/wide_pointer_comparisons.rs b/tests/ui/lint/wide_pointer_comparisons.rs index 8334575cf529..961b998c9566 100644 --- a/tests/ui/lint/wide_pointer_comparisons.rs +++ b/tests/ui/lint/wide_pointer_comparisons.rs @@ -107,6 +107,15 @@ fn main() { //~^ WARN ambiguous wide pointer comparison } + { + macro_rules! cmp { + ($a:tt, $b:tt) => { $a == $b } + //~^ WARN ambiguous wide pointer comparison + } + + cmp!(a, b); + } + { macro_rules! cmp { ($a:ident, $b:ident) => { $a == $b } diff --git a/tests/ui/lint/wide_pointer_comparisons.stderr b/tests/ui/lint/wide_pointer_comparisons.stderr index 926b8775902c..349ff467d0fb 100644 --- a/tests/ui/lint/wide_pointer_comparisons.stderr +++ b/tests/ui/lint/wide_pointer_comparisons.stderr @@ -421,7 +421,18 @@ LL | std::ptr::eq(*a, *b) | ~~~~~~~~~~~~~ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:112:39 + --> $DIR/wide_pointer_comparisons.rs:112:33 + | +LL | ($a:tt, $b:tt) => { $a == $b } + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | ($a:tt, $b:tt) => { std::ptr::addr_eq($a, $b) } + | ++++++++++++++++++ ~ + + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:121:39 | LL | ($a:ident, $b:ident) => { $a == $b } | ^^^^^^^^ @@ -436,7 +447,7 @@ LL | ($a:ident, $b:ident) => { std::ptr::addr_eq($a, $b) } | ++++++++++++++++++ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:122:37 + --> $DIR/wide_pointer_comparisons.rs:131:37 | LL | ($a:expr, $b:expr) => { $a == $b } | ^^ @@ -448,5 +459,5 @@ LL | cmp!(&a, &b); = help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses = note: this warning originates in the macro `cmp` (in Nightly builds, run with -Z macro-backtrace for more info) -warning: 37 warnings emitted +warning: 38 warnings emitted diff --git a/tests/ui/methods/method-on-ambiguous-numeric-type.rs b/tests/ui/methods/method-on-ambiguous-numeric-type.rs index 82f47438d505..f42b72e9f9c1 100644 --- a/tests/ui/methods/method-on-ambiguous-numeric-type.rs +++ b/tests/ui/methods/method-on-ambiguous-numeric-type.rs @@ -5,6 +5,9 @@ macro_rules! local_mac { ($ident:ident) => { let $ident = 42; } } +macro_rules! local_mac_tt { + ($tt:tt) => { let $tt = 42; } +} fn main() { let x = 2.0.neg(); @@ -23,6 +26,10 @@ fn main() { local_mac!(local_bar); local_bar.pow(2); //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` + + local_mac_tt!(local_bar_tt); + local_bar_tt.pow(2); + //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` } fn qux() { diff --git a/tests/ui/methods/method-on-ambiguous-numeric-type.stderr b/tests/ui/methods/method-on-ambiguous-numeric-type.stderr index 91733411637d..060595e1d406 100644 --- a/tests/ui/methods/method-on-ambiguous-numeric-type.stderr +++ b/tests/ui/methods/method-on-ambiguous-numeric-type.stderr @@ -1,5 +1,5 @@ error[E0689]: can't call method `neg` on ambiguous numeric type `{float}` - --> $DIR/method-on-ambiguous-numeric-type.rs:10:17 + --> $DIR/method-on-ambiguous-numeric-type.rs:13:17 | LL | let x = 2.0.neg(); | ^^^ @@ -10,7 +10,7 @@ LL | let x = 2.0_f32.neg(); | ~~~~~~~ error[E0689]: can't call method `neg` on ambiguous numeric type `{float}` - --> $DIR/method-on-ambiguous-numeric-type.rs:14:15 + --> $DIR/method-on-ambiguous-numeric-type.rs:17:15 | LL | let x = y.neg(); | ^^^ @@ -21,7 +21,7 @@ LL | let y: f32 = 2.0; | +++++ error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` - --> $DIR/method-on-ambiguous-numeric-type.rs:19:26 + --> $DIR/method-on-ambiguous-numeric-type.rs:22:26 | LL | for i in 0..100 { | - you must specify a type for this binding, like `i32` @@ -29,7 +29,7 @@ LL | println!("{}", i.pow(2)); | ^^^ error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` - --> $DIR/method-on-ambiguous-numeric-type.rs:24:15 + --> $DIR/method-on-ambiguous-numeric-type.rs:27:15 | LL | local_bar.pow(2); | ^^^ @@ -40,7 +40,18 @@ LL | ($ident:ident) => { let $ident: i32 = 42; } | +++++ error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` - --> $DIR/method-on-ambiguous-numeric-type.rs:30:9 + --> $DIR/method-on-ambiguous-numeric-type.rs:31:18 + | +LL | local_bar_tt.pow(2); + | ^^^ + | +help: you must specify a type for this binding, like `i32` + | +LL | ($tt:tt) => { let $tt: i32 = 42; } + | +++++ + +error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` + --> $DIR/method-on-ambiguous-numeric-type.rs:37:9 | LL | bar.pow(2); | ^^^ @@ -51,6 +62,6 @@ help: you must specify a type for this binding, like `i32` LL | ($ident:ident) => { let $ident: i32 = 42; } | +++++ -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0689`. From fb1cca29377d0ed227927e499d3ee6fe4b6af584 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 5 Jan 2024 17:29:57 +0300 Subject: [PATCH 101/257] parser: Tiny refactoring --- compiler/rustc_parse/src/parser/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0b24e7841263..da57ce01a568 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3735,7 +3735,7 @@ impl<'a> Parser<'a> { } pub(crate) fn mk_expr(&self, span: Span, kind: ExprKind) -> P { - P(Expr { kind, span, attrs: AttrVec::new(), id: DUMMY_NODE_ID, tokens: None }) + self.mk_expr_with_attrs(span, kind, AttrVec::new()) } pub(super) fn mk_expr_err(&self, span: Span) -> P { From 508d1ff7d84b625aef24c6a9cb25bbf6a76134d8 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 5 Jan 2024 17:30:14 +0300 Subject: [PATCH 102/257] rustc_span: More consistent span combination operations --- compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 4 +- compiler/rustc_span/src/lib.rs | 96 ++++++++++++------------- 3 files changed, 50 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index da57ce01a568..880743ddd3cf 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2489,7 +2489,7 @@ impl<'a> Parser<'a> { } ExprKind::Block(_, None) => { this.dcx().emit_err(errors::IfExpressionMissingCondition { - if_span: lo.shrink_to_hi(), + if_span: lo.with_neighbor(cond.span).shrink_to_hi(), block_span: self.sess.source_map().start_point(cond_span), }); std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi())) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 0ac0b678abac..2ce27ff66e1d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2118,7 +2118,7 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); err.span_suggestion( - span.shrink_to_hi(), + span.with_neighbor(self.token.span).shrink_to_hi(), "add a semicolon", ';', Applicability::MaybeIncorrect, @@ -2632,7 +2632,7 @@ impl<'a> Parser<'a> { let is_name_required = match this.token.kind { token::DotDotDot => false, - _ => req_name(this.token.span.edition()), + _ => req_name(this.token.span.with_neighbor(this.prev_token.span).edition()), }; let (pat, ty) = if is_name_required || this.is_named_param() { debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 8f64eed9a870..14f9a4ac2a97 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -826,6 +826,39 @@ impl Span { ) } + /// Prepare two spans to a combine operation like `to` or `between`. + /// FIXME: consider using declarative macro metavariable spans for the given spans if they are + /// better suitable for combining (#119412). + fn prepare_to_combine( + a_orig: Span, + b_orig: Span, + ) -> Result<(SpanData, SpanData, Option), Span> { + let (a, b) = (a_orig.data(), b_orig.data()); + + if a.ctxt != b.ctxt { + // Context mismatches usually happen when procedural macros combine spans copied from + // the macro input with spans produced by the macro (`Span::*_site`). + // In that case we consider the combined span to be produced by the macro and return + // the original macro-produced span as the result. + // Otherwise we just fall back to returning the first span. + // Combining locations typically doesn't make sense in case of context mismatches. + // `is_root` here is a fast path optimization. + let a_is_callsite = a.ctxt.is_root() || a.ctxt == b.span().source_callsite().ctxt(); + return Err(if a_is_callsite { b_orig } else { a_orig }); + } + + let parent = if a.parent == b.parent { a.parent } else { None }; + Ok((a, b, parent)) + } + + /// This span, but in a larger context, may switch to the metavariable span if suitable. + pub fn with_neighbor(self, neighbor: Span) -> Span { + match Span::prepare_to_combine(self, neighbor) { + Ok((this, ..)) => Span::new(this.lo, this.hi, this.ctxt, this.parent), + Err(_) => self, + } + } + /// Returns a `Span` that would enclose both `self` and `end`. /// /// Note that this can also be used to extend the span "backwards": @@ -837,26 +870,12 @@ impl Span { /// ^^^^^^^^^^^^^^^^^^^^ /// ``` pub fn to(self, end: Span) -> Span { - let span_data = self.data(); - let end_data = end.data(); - // FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480). - // Return the macro span on its own to avoid weird diagnostic output. It is preferable to - // have an incomplete span than a completely nonsensical one. - if span_data.ctxt != end_data.ctxt { - if span_data.ctxt.is_root() { - return end; - } else if end_data.ctxt.is_root() { - return self; + match Span::prepare_to_combine(self, end) { + Ok((from, to, parent)) => { + Span::new(cmp::min(from.lo, to.lo), cmp::max(from.hi, to.hi), from.ctxt, parent) } - // Both spans fall within a macro. - // FIXME(estebank): check if it is the *same* macro. + Err(fallback) => fallback, } - Span::new( - cmp::min(span_data.lo, end_data.lo), - cmp::max(span_data.hi, end_data.hi), - if span_data.ctxt.is_root() { end_data.ctxt } else { span_data.ctxt }, - if span_data.parent == end_data.parent { span_data.parent } else { None }, - ) } /// Returns a `Span` between the end of `self` to the beginning of `end`. @@ -867,14 +886,12 @@ impl Span { /// ^^^^^^^^^^^^^ /// ``` pub fn between(self, end: Span) -> Span { - let span = self.data(); - let end = end.data(); - Span::new( - span.hi, - end.lo, - if end.ctxt.is_root() { end.ctxt } else { span.ctxt }, - if span.parent == end.parent { span.parent } else { None }, - ) + match Span::prepare_to_combine(self, end) { + Ok((from, to, parent)) => { + Span::new(cmp::min(from.hi, to.hi), cmp::max(from.lo, to.lo), from.ctxt, parent) + } + Err(fallback) => fallback, + } } /// Returns a `Span` from the beginning of `self` until the beginning of `end`. @@ -885,31 +902,12 @@ impl Span { /// ^^^^^^^^^^^^^^^^^ /// ``` pub fn until(self, end: Span) -> Span { - // Most of this function's body is copied from `to`. - // We can't just do `self.to(end.shrink_to_lo())`, - // because to also does some magic where it uses min/max so - // it can handle overlapping spans. Some advanced mis-use of - // `until` with different ctxts makes this visible. - let span_data = self.data(); - let end_data = end.data(); - // FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480). - // Return the macro span on its own to avoid weird diagnostic output. It is preferable to - // have an incomplete span than a completely nonsensical one. - if span_data.ctxt != end_data.ctxt { - if span_data.ctxt.is_root() { - return end; - } else if end_data.ctxt.is_root() { - return self; + match Span::prepare_to_combine(self, end) { + Ok((from, to, parent)) => { + Span::new(cmp::min(from.lo, to.lo), cmp::max(from.lo, to.lo), from.ctxt, parent) } - // Both spans fall within a macro. - // FIXME(estebank): check if it is the *same* macro. + Err(fallback) => fallback, } - Span::new( - span_data.lo, - end_data.lo, - if end_data.ctxt.is_root() { end_data.ctxt } else { span_data.ctxt }, - if span_data.parent == end_data.parent { span_data.parent } else { None }, - ) } pub fn from_inner(self, inner: InnerSpan) -> Span { From 97c82385114dfb8aa30b0fd63aa06c201aeb65d9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jan 2024 18:49:25 +0100 Subject: [PATCH 103/257] remove duplicate test --- tests/ui/numbers-arithmetic/overflowing-rsh-6.rs | 9 --------- .../ui/numbers-arithmetic/overflowing-rsh-6.stderr | 14 -------------- 2 files changed, 23 deletions(-) delete mode 100644 tests/ui/numbers-arithmetic/overflowing-rsh-6.rs delete mode 100644 tests/ui/numbers-arithmetic/overflowing-rsh-6.stderr diff --git a/tests/ui/numbers-arithmetic/overflowing-rsh-6.rs b/tests/ui/numbers-arithmetic/overflowing-rsh-6.rs deleted file mode 100644 index f75e779ed158..000000000000 --- a/tests/ui/numbers-arithmetic/overflowing-rsh-6.rs +++ /dev/null @@ -1,9 +0,0 @@ -// build-fail -// compile-flags: -C debug-assertions - -#![deny(arithmetic_overflow)] - -fn main() { - let _n = 1i64 >> [64][0]; - //~^ ERROR: this arithmetic operation will overflow -} diff --git a/tests/ui/numbers-arithmetic/overflowing-rsh-6.stderr b/tests/ui/numbers-arithmetic/overflowing-rsh-6.stderr deleted file mode 100644 index 005f7378226c..000000000000 --- a/tests/ui/numbers-arithmetic/overflowing-rsh-6.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: this arithmetic operation will overflow - --> $DIR/overflowing-rsh-6.rs:7:14 - | -LL | let _n = 1i64 >> [64][0]; - | ^^^^^^^^^^^^^^^ attempt to shift right by `64_i32`, which would overflow - | -note: the lint level is defined here - --> $DIR/overflowing-rsh-6.rs:4:9 - | -LL | #![deny(arithmetic_overflow)] - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - From a10b3cd60f426ac94164667ee10f6f3ea2fed27a Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Fri, 5 Jan 2024 19:17:55 +0000 Subject: [PATCH 104/257] Fix broken build for ESP IDF due to #119026 --- library/std/src/os/unix/net/listener.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 1b70b669c779..f6835472e2ca 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -73,7 +73,7 @@ impl UnixListener { unsafe { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; - #[cfg(any(target_os = "windows", target_os = "redox"))] + #[cfg(any(target_os = "windows", target_os = "redox", target_os = "espidf"))] const backlog: libc::c_int = 128; #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] const backlog: libc::c_int = -1; @@ -82,7 +82,8 @@ impl UnixListener { target_os = "redox", target_os = "linux", target_os = "freebsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "espidf" )))] const backlog: libc::c_int = libc::SOMAXCONN; From 54967d7a680bc72bea2c1ff6f1edc0d7d2ca6d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 5 Jan 2024 20:33:16 +0100 Subject: [PATCH 105/257] Ignore a rustdoc test --- tests/rustdoc/synthetic_auto/no-redundancy.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/rustdoc/synthetic_auto/no-redundancy.rs b/tests/rustdoc/synthetic_auto/no-redundancy.rs index ea57d7388b85..fed9c9c7ba47 100644 --- a/tests/rustdoc/synthetic_auto/no-redundancy.rs +++ b/tests/rustdoc/synthetic_auto/no-redundancy.rs @@ -1,3 +1,6 @@ +// FIXME(fmease, #119216): Reenable this test! +// ignore-test + pub struct Inner { field: T, } From 95eb5bcb67b97e7a1f3f6e6e68b3a8a0737ca24e Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Wed, 3 Jan 2024 06:35:31 +0100 Subject: [PATCH 106/257] rustc_mir_transform: Make DestinationPropagation stable for queries By using FxIndexMap instead of FxHashMap, so that the order of visiting of locals is deterministic. We also need to bless copy_propagation_arg.foo.DestinationPropagation.panic*.diff. Do not review the diff of the diff. Instead look at the diff file before and after this commit. Both before and after this commit, 3 statements are replaced with nop. It's just that due to change in ordering, different statements are replaced. But the net result is the same. --- compiler/rustc_data_structures/src/fx.rs | 1 + compiler/rustc_mir_transform/src/dest_prop.rs | 30 +++++++++---------- ...oo.DestinationPropagation.panic-abort.diff | 18 +++++------ ...o.DestinationPropagation.panic-unwind.diff | 18 +++++------ 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs index 9fce0e1e65cc..80e72250470c 100644 --- a/compiler/rustc_data_structures/src/fx.rs +++ b/compiler/rustc_data_structures/src/fx.rs @@ -7,6 +7,7 @@ pub type StdEntry<'a, K, V> = std::collections::hash_map::Entry<'a, K, V>; pub type FxIndexMap = indexmap::IndexMap>; pub type FxIndexSet = indexmap::IndexSet>; pub type IndexEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>; +pub type IndexOccupiedEntry<'a, K, V> = indexmap::map::OccupiedEntry<'a, K, V>; #[macro_export] macro_rules! define_id_collections { diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index cd80f423466b..49b0edc0db88 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -131,10 +131,8 @@ //! [attempt 2]: https://github.com/rust-lang/rust/pull/71003 //! [attempt 3]: https://github.com/rust-lang/rust/pull/72632 -use std::collections::hash_map::{Entry, OccupiedEntry}; - use crate::MirPass; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry, IndexOccupiedEntry}; use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::HasLocalDecls; @@ -211,7 +209,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { let mut merged_locals: BitSet = BitSet::new_empty(body.local_decls.len()); // This is the set of merges we will apply this round. It is a subset of the candidates. - let mut merges = FxHashMap::default(); + let mut merges = FxIndexMap::default(); for (src, candidates) in candidates.c.iter() { if merged_locals.contains(*src) { @@ -250,8 +248,8 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { /// frequently. Everything with a `&'alloc` lifetime points into here. #[derive(Default)] struct Allocations { - candidates: FxHashMap>, - candidates_reverse: FxHashMap>, + candidates: FxIndexMap>, + candidates_reverse: FxIndexMap>, write_info: WriteInfo, // PERF: Do this for `MaybeLiveLocals` allocations too. } @@ -272,11 +270,11 @@ struct Candidates<'alloc> { /// /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to /// remove that assignment. - c: &'alloc mut FxHashMap>, + c: &'alloc mut FxIndexMap>, /// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`, /// then this contains `b => a`. // PERF: Possibly these should be `SmallVec`s? - reverse: &'alloc mut FxHashMap>, + reverse: &'alloc mut FxIndexMap>, } ////////////////////////////////////////////////////////// @@ -287,7 +285,7 @@ struct Candidates<'alloc> { fn apply_merges<'tcx>( body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>, - merges: &FxHashMap, + merges: &FxIndexMap, merged_locals: &BitSet, ) { let mut merger = Merger { tcx, merges, merged_locals }; @@ -296,7 +294,7 @@ fn apply_merges<'tcx>( struct Merger<'a, 'tcx> { tcx: TyCtxt<'tcx>, - merges: &'a FxHashMap, + merges: &'a FxIndexMap, merged_locals: &'a BitSet, } @@ -379,7 +377,7 @@ impl<'alloc> Candidates<'alloc> { /// `vec_filter_candidates` but for an `Entry` fn entry_filter_candidates( - mut entry: OccupiedEntry<'_, Local, Vec>, + mut entry: IndexOccupiedEntry<'_, Local, Vec>, p: Local, f: impl FnMut(Local) -> CandidateFilter, at: Location, @@ -399,7 +397,7 @@ impl<'alloc> Candidates<'alloc> { at: Location, ) { // Cover the cases where `p` appears as a `src` - if let Entry::Occupied(entry) = self.c.entry(p) { + if let IndexEntry::Occupied(entry) = self.c.entry(p) { Self::entry_filter_candidates(entry, p, &mut f, at); } // And the cases where `p` appears as a `dest` @@ -412,7 +410,7 @@ impl<'alloc> Candidates<'alloc> { if f(*src) == CandidateFilter::Keep { return true; } - let Entry::Occupied(entry) = self.c.entry(*src) else { + let IndexEntry::Occupied(entry) = self.c.entry(*src) else { return false; }; Self::entry_filter_candidates( @@ -721,8 +719,8 @@ fn places_to_candidate_pair<'tcx>( fn find_candidates<'alloc, 'tcx>( body: &Body<'tcx>, borrowed: &BitSet, - candidates: &'alloc mut FxHashMap>, - candidates_reverse: &'alloc mut FxHashMap>, + candidates: &'alloc mut FxIndexMap>, + candidates_reverse: &'alloc mut FxIndexMap>, ) -> Candidates<'alloc> { candidates.clear(); candidates_reverse.clear(); @@ -744,7 +742,7 @@ fn find_candidates<'alloc, 'tcx>( struct FindAssignments<'a, 'alloc, 'tcx> { body: &'a Body<'tcx>, - candidates: &'alloc mut FxHashMap>, + candidates: &'alloc mut FxIndexMap>, borrowed: &'a BitSet, } diff --git a/tests/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.panic-abort.diff index 54875cadec5b..b461869be318 100644 --- a/tests/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.panic-abort.diff @@ -8,20 +8,20 @@ let mut _3: u8; bb0: { -- StorageLive(_2); -+ nop; - StorageLive(_3); - _3 = _1; + StorageLive(_2); +- StorageLive(_3); +- _3 = _1; - _2 = dummy(move _3) -> [return: bb1, unwind unreachable]; -+ _1 = dummy(move _3) -> [return: bb1, unwind unreachable]; ++ nop; ++ nop; ++ _2 = dummy(move _1) -> [return: bb1, unwind unreachable]; } bb1: { - StorageDead(_3); -- _1 = move _2; -- StorageDead(_2); -+ nop; +- StorageDead(_3); + nop; + _1 = move _2; + StorageDead(_2); _0 = const (); return; } diff --git a/tests/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.panic-unwind.diff index b4c8a89278b9..d5c2e07c6c23 100644 --- a/tests/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.panic-unwind.diff @@ -8,20 +8,20 @@ let mut _3: u8; bb0: { -- StorageLive(_2); -+ nop; - StorageLive(_3); - _3 = _1; + StorageLive(_2); +- StorageLive(_3); +- _3 = _1; - _2 = dummy(move _3) -> [return: bb1, unwind continue]; -+ _1 = dummy(move _3) -> [return: bb1, unwind continue]; ++ nop; ++ nop; ++ _2 = dummy(move _1) -> [return: bb1, unwind continue]; } bb1: { - StorageDead(_3); -- _1 = move _2; -- StorageDead(_2); -+ nop; +- StorageDead(_3); + nop; + _1 = move _2; + StorageDead(_2); _0 = const (); return; } From 004bfc5eb2aa6789741055d3e6c479e34eb960cb Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 5 Jan 2024 12:40:11 -0700 Subject: [PATCH 107/257] Add notes about the serialization format --- src/librustdoc/html/render/mod.rs | 31 ++++++++++++++++++++----- src/librustdoc/html/static/js/search.js | 2 +- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8afeec81d873..bea5ccd7c860 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -131,6 +131,10 @@ pub(crate) struct RenderType { } impl RenderType { + // Types are rendered as lists of lists, because that's pretty compact. + // The contents of the lists are always integers in self-terminating hex + // form, handled by `RenderTypeId::write_to_string`, so no commas are + // needed to separate the items. pub fn write_to_string(&self, string: &mut String) { fn write_optional_id(id: Option, string: &mut String) { // 0 is a sentinel, everything else is one-indexed @@ -139,6 +143,9 @@ impl RenderType { None => string.push('`'), } } + // Either just the type id, or `{type, generics, bindings?}` + // where generics is a list of types, + // and bindings is a list of `{id, typelist}` pairs. if self.generics.is_some() || self.bindings.is_some() { string.push('{'); write_optional_id(self.id, string); @@ -186,10 +193,19 @@ impl RenderTypeId { RenderTypeId::Index(idx) => (true, (-*idx).try_into().unwrap()), _ => panic!("must convert render types to indexes before serializing"), }; - // zig-zag notation + // zig-zag encoding let value: u32 = (id << 1) | (if sign { 1 } else { 0 }); - // encode - // Documented in https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html + // Self-terminating hex use capital letters for everything but the + // least significant digit, which is lowercase. For example, decimal 17 + // would be `` Aa `` if zig-zag encoding weren't used. + // + // Zig-zag encoding, however, stores the sign bit as the last bit. + // This means, in the last hexit, 1 is actually `c`, -1 is `b` + // (`a` is the imaginary -0), and, because all the bits are shifted + // by one, `` A` `` is actually 8 and `` Aa `` is -8. + // + // https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html + // describes the encoding in more detail. let mut shift: u32 = 28; let mut mask: u32 = 0xF0_00_00_00; while shift < 32 { @@ -219,8 +235,9 @@ impl IndexItemFunctionType { string: &mut String, backref_queue: &mut VecDeque<&'a IndexItemFunctionType>, ) { - assert!(backref_queue.len() < 16); - // If we couldn't figure out a type, just write `0`. + assert!(backref_queue.len() <= 16); + // If we couldn't figure out a type, just write 0, + // which is encoded as `` ` `` (see RenderTypeId::write_to_string). let has_missing = self .inputs .iter() @@ -229,13 +246,15 @@ impl IndexItemFunctionType { if has_missing { string.push('`'); } else if let Some(idx) = backref_queue.iter().position(|other| *other == self) { + // The backref queue has 16 items, so backrefs use + // a single hexit, disjoint from the ones used for numbers. string.push( char::try_from('0' as u32 + u32::try_from(idx).unwrap()) .expect("last possible value is '?'"), ); } else { backref_queue.push_front(self); - if backref_queue.len() >= 16 { + if backref_queue.len() > 16 { backref_queue.pop_back(); } string.push('{'); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 6fb92d8fbb12..e0708485fc05 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2858,7 +2858,7 @@ ${item.displayPath}${name}\ inputs, output, where_clause, }; itemFunctionDecoder.backrefQueue.unshift(ret); - if (itemFunctionDecoder.backrefQueue.length >= 16) { + if (itemFunctionDecoder.backrefQueue.length > 16) { itemFunctionDecoder.backrefQueue.pop(); } return ret; From 274674819c3c37e09f5fe0e3276d3921068dbb95 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 5 Jan 2024 21:50:52 +0100 Subject: [PATCH 108/257] fix OOM when `ty::Instance` is used in query description --- compiler/rustc_middle/src/ty/instance.rs | 12 ++++++++---- .../auxiliary/suggest-constructor-cycle-error.rs | 12 ++++++++++++ .../ui/resolve/suggest-constructor-cycle-error.rs | 10 ++++++++++ .../suggest-constructor-cycle-error.stderr | 15 +++++++++++++++ 4 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 tests/ui/resolve/auxiliary/suggest-constructor-cycle-error.rs create mode 100644 tests/ui/resolve/suggest-constructor-cycle-error.rs create mode 100644 tests/ui/resolve/suggest-constructor-cycle-error.stderr diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 2ac3cddfa15a..dd41cb5a61f4 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -293,12 +293,16 @@ impl<'tcx> InstanceDef<'tcx> { fn fmt_instance( f: &mut fmt::Formatter<'_>, instance: &Instance<'_>, - type_length: rustc_session::Limit, + type_length: Option, ) -> fmt::Result { ty::tls::with(|tcx| { let args = tcx.lift(instance.args).expect("could not lift for printing"); - let mut cx = FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length); + let mut cx = if let Some(type_length) = type_length { + FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length) + } else { + FmtPrinter::new(tcx, Namespace::ValueNS) + }; cx.print_def_path(instance.def_id(), args)?; let s = cx.into_buffer(); f.write_str(&s) @@ -324,13 +328,13 @@ pub struct ShortInstance<'a, 'tcx>(pub &'a Instance<'tcx>, pub usize); impl<'a, 'tcx> fmt::Display for ShortInstance<'a, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_instance(f, self.0, rustc_session::Limit(self.1)) + fmt_instance(f, self.0, Some(rustc_session::Limit(self.1))) } } impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| fmt_instance(f, self, tcx.type_length_limit())) + fmt_instance(f, self, None) } } diff --git a/tests/ui/resolve/auxiliary/suggest-constructor-cycle-error.rs b/tests/ui/resolve/auxiliary/suggest-constructor-cycle-error.rs new file mode 100644 index 000000000000..8de68c38bc34 --- /dev/null +++ b/tests/ui/resolve/auxiliary/suggest-constructor-cycle-error.rs @@ -0,0 +1,12 @@ +mod m { + pub struct Uuid(()); + + impl Uuid { + pub fn encode_buffer() -> [u8; LENGTH] { + [] + } + } + const LENGTH: usize = 0; +} + +pub use m::Uuid; diff --git a/tests/ui/resolve/suggest-constructor-cycle-error.rs b/tests/ui/resolve/suggest-constructor-cycle-error.rs new file mode 100644 index 000000000000..2336cd92f4d3 --- /dev/null +++ b/tests/ui/resolve/suggest-constructor-cycle-error.rs @@ -0,0 +1,10 @@ +// aux-build:suggest-constructor-cycle-error.rs +//~^ cycle detected when getting the resolver for lowering [E0391] + +// Regression test for https://github.com/rust-lang/rust/issues/119625 + +extern crate suggest_constructor_cycle_error as a; + +const CONST_NAME: a::Uuid = a::Uuid(()); + +fn main() {} diff --git a/tests/ui/resolve/suggest-constructor-cycle-error.stderr b/tests/ui/resolve/suggest-constructor-cycle-error.stderr new file mode 100644 index 000000000000..ae139619c695 --- /dev/null +++ b/tests/ui/resolve/suggest-constructor-cycle-error.stderr @@ -0,0 +1,15 @@ +error[E0391]: cycle detected when getting the resolver for lowering + | + = note: ...which requires normalizing `[u8; suggest_constructor_cycle_error::::m::{impl#0}::encode_buffer::{constant#0}]`... +note: ...which requires resolving instance `suggest_constructor_cycle_error::m::Uuid::encode_buffer::{constant#0}`... + --> $DIR/auxiliary/suggest-constructor-cycle-error.rs:5:40 + | +LL | pub fn encode_buffer() -> [u8; LENGTH] { + | ^^^^^^ + = note: ...which requires calculating the lang items map... + = note: ...which again requires getting the resolver for lowering, completing the cycle + = 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: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. From 339fa311ad8140cfb98a1c6080ea7e002b9ce3fa Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 5 Jan 2024 21:55:09 +0100 Subject: [PATCH 109/257] fix cycle error for "use constructor" suggestion --- .../rustc_resolve/src/late/diagnostics.rs | 7 ++----- .../suggest-constructor-cycle-error.rs | 2 +- .../suggest-constructor-cycle-error.stderr | 20 +++++++++---------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6c38ed622703..1ccb4820cdd9 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1755,11 +1755,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .filter_map(|item| { // Only assoc fns that return `Self` let fn_sig = self.r.tcx.fn_sig(item.def_id).skip_binder(); - let ret_ty = fn_sig.output(); - let ret_ty = self - .r - .tcx - .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), ret_ty); + // Don't normalize the return type, because that can cause cycle errors. + let ret_ty = fn_sig.output().skip_binder(); let ty::Adt(def, _args) = ret_ty.kind() else { return None; }; diff --git a/tests/ui/resolve/suggest-constructor-cycle-error.rs b/tests/ui/resolve/suggest-constructor-cycle-error.rs index 2336cd92f4d3..e36fffd97d1b 100644 --- a/tests/ui/resolve/suggest-constructor-cycle-error.rs +++ b/tests/ui/resolve/suggest-constructor-cycle-error.rs @@ -1,10 +1,10 @@ // aux-build:suggest-constructor-cycle-error.rs -//~^ cycle detected when getting the resolver for lowering [E0391] // Regression test for https://github.com/rust-lang/rust/issues/119625 extern crate suggest_constructor_cycle_error as a; const CONST_NAME: a::Uuid = a::Uuid(()); +//~^ ERROR: cannot initialize a tuple struct which contains private fields [E0423] fn main() {} diff --git a/tests/ui/resolve/suggest-constructor-cycle-error.stderr b/tests/ui/resolve/suggest-constructor-cycle-error.stderr index ae139619c695..c6ec2465a432 100644 --- a/tests/ui/resolve/suggest-constructor-cycle-error.stderr +++ b/tests/ui/resolve/suggest-constructor-cycle-error.stderr @@ -1,15 +1,15 @@ -error[E0391]: cycle detected when getting the resolver for lowering +error[E0423]: cannot initialize a tuple struct which contains private fields + --> $DIR/suggest-constructor-cycle-error.rs:7:29 | - = note: ...which requires normalizing `[u8; suggest_constructor_cycle_error::::m::{impl#0}::encode_buffer::{constant#0}]`... -note: ...which requires resolving instance `suggest_constructor_cycle_error::m::Uuid::encode_buffer::{constant#0}`... - --> $DIR/auxiliary/suggest-constructor-cycle-error.rs:5:40 +LL | const CONST_NAME: a::Uuid = a::Uuid(()); + | ^^^^^^^ | -LL | pub fn encode_buffer() -> [u8; LENGTH] { - | ^^^^^^ - = note: ...which requires calculating the lang items map... - = note: ...which again requires getting the resolver for lowering, completing the cycle - = 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 +note: constructor is not visible here due to private fields + --> $DIR/auxiliary/suggest-constructor-cycle-error.rs:2:21 + | +LL | pub struct Uuid(()); + | ^^ private field error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0391`. +For more information about this error, try `rustc --explain E0423`. From 7366bdaea2757b7b45965f4b59a8137adf7d10af Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 29 Dec 2023 20:04:03 +0000 Subject: [PATCH 110/257] Handle ForeignItem as TAIT scope. --- .../src/collect/type_of/opaque.rs | 7 +++++++ .../nested-in-anon-const.rs | 21 +++++++++++++++++++ .../nested-in-anon-const.stderr | 20 ++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 tests/ui/type-alias-impl-trait/nested-in-anon-const.rs create mode 100644 tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 5a73097b0f6b..04047e56c60e 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -69,6 +69,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local Node::Item(it) => locator.visit_item(it), Node::ImplItem(it) => locator.visit_impl_item(it), Node::TraitItem(it) => locator.visit_trait_item(it), + Node::ForeignItem(it) => locator.visit_foreign_item(it), other => bug!("{:?} is not a valid scope for an opaque type item", other), } } @@ -240,6 +241,12 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { self.check(it.owner_id.def_id); intravisit::walk_trait_item(self, it); } + fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { + trace!(?it.owner_id); + assert_ne!(it.owner_id.def_id, self.def_id); + self.check(it.owner_id.def_id); + intravisit::walk_foreign_item(self, it); + } } pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( diff --git a/tests/ui/type-alias-impl-trait/nested-in-anon-const.rs b/tests/ui/type-alias-impl-trait/nested-in-anon-const.rs new file mode 100644 index 000000000000..e9d53c99d041 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/nested-in-anon-const.rs @@ -0,0 +1,21 @@ +// Regression test for issue #119295. + +#![feature(type_alias_impl_trait)] + +type Bar = T; +type S = [i32; A]; + +extern "C" { + pub fn lint_me( + x: Bar< + S< + { //~ ERROR mismatched types + type B = impl Sized; + //~^ ERROR unconstrained opaque type + }, + >, + >, + ); +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr b/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr new file mode 100644 index 000000000000..aa0c1076117c --- /dev/null +++ b/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/nested-in-anon-const.rs:12:17 + | +LL | / { +LL | | type B = impl Sized; +LL | | +LL | | }, + | |_________________^ expected `usize`, found `()` + +error: unconstrained opaque type + --> $DIR/nested-in-anon-const.rs:13:33 + | +LL | type B = impl Sized; + | ^^^^^^^^^^ + | + = note: `B` must be used in combination with a concrete type within the same item + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From 6dfdeab65a5e08efa811e033417bd7aef5d3c1fa Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 31 Dec 2023 00:22:52 +0000 Subject: [PATCH 111/257] Do not run check on foreign items. --- compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 04047e56c60e..da7279967dac 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -244,7 +244,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { trace!(?it.owner_id); assert_ne!(it.owner_id.def_id, self.def_id); - self.check(it.owner_id.def_id); + // No need to call `check`, as we do not run borrowck on foreign items. intravisit::walk_foreign_item(self, it); } } From eeaea576008f53e259c0f86cdd37f28bed9034a1 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 5 Jan 2024 21:54:41 +0000 Subject: [PATCH 112/257] Rebase fallout. --- .../nested-in-anon-const.stderr | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr b/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr index aa0c1076117c..d0fe920b35f4 100644 --- a/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr +++ b/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr @@ -1,3 +1,11 @@ +error: unconstrained opaque type + --> $DIR/nested-in-anon-const.rs:13:33 + | +LL | type B = impl Sized; + | ^^^^^^^^^^ + | + = note: `B` must be used in combination with a concrete type within the same item + error[E0308]: mismatched types --> $DIR/nested-in-anon-const.rs:12:17 | @@ -7,14 +15,6 @@ LL | | LL | | }, | |_________________^ expected `usize`, found `()` -error: unconstrained opaque type - --> $DIR/nested-in-anon-const.rs:13:33 - | -LL | type B = impl Sized; - | ^^^^^^^^^^ - | - = note: `B` must be used in combination with a concrete type within the same item - error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. From 90d11d64486bc758ee15a1dd5dba351447648097 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 2 Jan 2024 23:32:40 +0300 Subject: [PATCH 113/257] rustc_span: Optimize syntax context comparisons Including comparisons with root context --- compiler/rustc_expand/src/mbe/quoted.rs | 2 +- compiler/rustc_lint/src/internal.rs | 4 +- compiler/rustc_lint/src/non_fmt_panic.rs | 5 +- compiler/rustc_passes/src/liveness.rs | 2 +- compiler/rustc_span/src/hygiene.rs | 10 ++-- compiler/rustc_span/src/lib.rs | 36 +++++++------- compiler/rustc_span/src/source_map.rs | 12 +++-- compiler/rustc_span/src/source_map/tests.rs | 2 +- compiler/rustc_span/src/span_encoding.rs | 47 +++++++++++++------ .../src/casts/unnecessary_cast.rs | 2 +- .../clippy_lints/src/implicit_hasher.rs | 2 +- .../clippy_lints/src/implicit_return.rs | 2 +- .../src/methods/option_map_unwrap_or.rs | 2 +- 13 files changed, 76 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 445be01bc97c..cd4ba7a9a62b 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -72,7 +72,7 @@ pub(super) fn parse( // `SyntaxContext::root()` from a foreign crate will // have the edition of that crate (which we manually // retrieve via the `edition` parameter). - if span.ctxt().is_root() { + if !span.from_expansion() { edition } else { span.edition() diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 53d99c7f7f34..e3405aa2e559 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -549,7 +549,9 @@ declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]); impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if let ExprKind::Binary(BinOp { node: BinOpKind::Eq, .. }, lhs, rhs) = expr.kind { + if let ExprKind::Binary(BinOp { node: BinOpKind::Eq | BinOpKind::Ne, .. }, lhs, rhs) = + expr.kind + { if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) { cx.emit_spanned_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag); } diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index f0bbc03d7477..479acd88d718 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -111,10 +111,11 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc let mut arg_span = arg.span; let mut arg_macro = None; while !span.contains(arg_span) { - let expn = arg_span.ctxt().outer_expn_data(); - if expn.is_root() { + let ctxt = arg_span.ctxt(); + if ctxt.is_root() { break; } + let expn = ctxt.outer_expn_data(); arg_macro = expn.macro_def_id; arg_span = expn.call_site; } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index cfe829f170f7..75ef1cd38e82 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1630,7 +1630,7 @@ impl<'tcx> Liveness<'_, 'tcx> { let from_macro = non_shorthands .iter() .find(|(_, pat_span, ident_span)| { - pat_span.ctxt() != ident_span.ctxt() && pat_span.from_expansion() + !pat_span.eq_ctxt(*ident_span) && pat_span.from_expansion() }) .map(|(_, pat_span, _)| *pat_span); let non_shorthands = non_shorthands diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index b717229b68df..72642fa27bb7 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -295,11 +295,13 @@ impl ExpnId { pub fn expansion_cause(mut self) -> Option { let mut last_macro = None; loop { + // Fast path to avoid locking. + if self == ExpnId::root() { + break; + } let expn_data = self.expn_data(); // Stop going up the backtrace once include! is encountered - if expn_data.is_root() - || expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include) - { + if expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include) { break; } self = expn_data.call_site.ctxt().outer_expn(); @@ -433,7 +435,7 @@ impl HygieneData { fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> { let mut marks = Vec::new(); - while ctxt != SyntaxContext::root() { + while !ctxt.is_root() { debug!("marks: getting parent of {:?}", ctxt); marks.push(self.outer_mark(ctxt)); ctxt = self.parent_ctxt(ctxt); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 8f64eed9a870..3dc5434c7aff 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -541,10 +541,6 @@ impl Span { self.data().with_hi(hi) } #[inline] - pub fn eq_ctxt(self, other: Span) -> bool { - self.data_untracked().ctxt == other.data_untracked().ctxt - } - #[inline] pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span { self.data_untracked().with_ctxt(ctxt) } @@ -565,7 +561,7 @@ impl Span { /// Returns `true` if this span comes from any kind of macro, desugaring or inlining. #[inline] pub fn from_expansion(self) -> bool { - self.ctxt() != SyntaxContext::root() + !self.ctxt().is_root() } /// Returns `true` if `span` originates in a macro's expansion where debuginfo should be @@ -654,15 +650,15 @@ impl Span { /// Returns the source span -- this is either the supplied span, or the span for /// the macro callsite that expanded to it. pub fn source_callsite(self) -> Span { - let expn_data = self.ctxt().outer_expn_data(); - if !expn_data.is_root() { expn_data.call_site.source_callsite() } else { self } + let ctxt = self.ctxt(); + if !ctxt.is_root() { ctxt.outer_expn_data().call_site.source_callsite() } else { self } } /// The `Span` for the tokens in the previous macro expansion from which `self` was generated, /// if any. pub fn parent_callsite(self) -> Option { - let expn_data = self.ctxt().outer_expn_data(); - if !expn_data.is_root() { Some(expn_data.call_site) } else { None } + let ctxt = self.ctxt(); + (!ctxt.is_root()).then(|| ctxt.outer_expn_data().call_site) } /// Walk down the expansion ancestors to find a span that's contained within `outer`. @@ -747,15 +743,14 @@ impl Span { /// else returns the `ExpnData` for the macro definition /// corresponding to the source callsite. pub fn source_callee(self) -> Option { - let expn_data = self.ctxt().outer_expn_data(); - - // Create an iterator of call site expansions - iter::successors(Some(expn_data), |expn_data| { - Some(expn_data.call_site.ctxt().outer_expn_data()) - }) - // Find the last expansion which is not root - .take_while(|expn_data| !expn_data.is_root()) - .last() + let mut ctxt = self.ctxt(); + let mut opt_expn_data = None; + while !ctxt.is_root() { + let expn_data = ctxt.outer_expn_data(); + ctxt = expn_data.call_site.ctxt(); + opt_expn_data = Some(expn_data); + } + opt_expn_data } /// Checks if a span is "internal" to a macro in which `#[unstable]` @@ -796,11 +791,12 @@ impl Span { let mut prev_span = DUMMY_SP; iter::from_fn(move || { loop { - let expn_data = self.ctxt().outer_expn_data(); - if expn_data.is_root() { + let ctxt = self.ctxt(); + if ctxt.is_root() { return None; } + let expn_data = ctxt.outer_expn_data(); let is_recursive = expn_data.call_site.source_equal(prev_span); prev_span = self; diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index c61dbcaae954..8253ffefcaa7 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -23,9 +23,15 @@ mod tests; /// otherwise return the call site span up to the `enclosing_sp` by /// following the `expn_data` chain. pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span { - let expn_data1 = sp.ctxt().outer_expn_data(); - let expn_data2 = enclosing_sp.ctxt().outer_expn_data(); - if expn_data1.is_root() || !expn_data2.is_root() && expn_data1.call_site == expn_data2.call_site + let ctxt = sp.ctxt(); + if ctxt.is_root() { + return sp; + } + + let enclosing_ctxt = enclosing_sp.ctxt(); + let expn_data1 = ctxt.outer_expn_data(); + if !enclosing_ctxt.is_root() + && expn_data1.call_site == enclosing_ctxt.outer_expn_data().call_site { sp } else { diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 130522a302dd..5788d11ed43c 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -18,7 +18,7 @@ impl SourceMap { /// * the LHS span must start at or before the RHS span. fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option { // Ensure we're at the same expansion ID. - if sp_lhs.ctxt() != sp_rhs.ctxt() { + if !sp_lhs.eq_ctxt(sp_rhs) { return None; } diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index f7d17a267d69..e162695a13be 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -210,12 +210,10 @@ impl Span { } } - /// This function is used as a fast path when decoding the full `SpanData` is not necessary. - /// It's a cut-down version of `data_untracked`. - #[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")] - #[inline] - pub fn ctxt(self) -> SyntaxContext { - if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER { + // Returns either syntactic context, if it can be retrieved without taking the interner lock, + // or an index into the interner if it cannot. + fn inline_ctxt(self) -> Result { + Ok(if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER { if self.len_with_tag_or_marker & PARENT_TAG == 0 { // Inline-context format. SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32) @@ -223,17 +221,36 @@ impl Span { // Inline-parent format. We know that the SyntaxContext is root. SyntaxContext::root() } + } else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER { + // Partially-interned format. This path avoids looking up the + // interned value, and is the whole point of the + // partially-interned format. + SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32) } else { - if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER { - // Partially-interned format. This path avoids looking up the - // interned value, and is the whole point of the - // partially-interned format. - SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32) - } else { - // Fully-interned format. - let index = self.lo_or_index; - with_span_interner(|interner| interner.spans[index as usize].ctxt) + // Fully-interned format. + return Err(self.lo_or_index as usize); + }) + } + + /// This function is used as a fast path when decoding the full `SpanData` is not necessary. + /// It's a cut-down version of `data_untracked`. + #[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")] + #[inline] + pub fn ctxt(self) -> SyntaxContext { + self.inline_ctxt() + .unwrap_or_else(|index| with_span_interner(|interner| interner.spans[index].ctxt)) + } + + #[inline] + pub fn eq_ctxt(self, other: Span) -> bool { + match (self.inline_ctxt(), other.inline_ctxt()) { + (Ok(ctxt1), Ok(ctxt2)) => ctxt1 == ctxt2, + (Ok(ctxt), Err(index)) | (Err(index), Ok(ctxt)) => { + with_span_interner(|interner| ctxt == interner.spans[index].ctxt) } + (Err(index1), Err(index2)) => with_span_interner(|interner| { + interner.spans[index1].ctxt == interner.spans[index2].ctxt + }), } } } diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 849920bb76d5..3761ba81f521 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -145,7 +145,7 @@ pub(super) fn check<'tcx>( if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { if let Some(id) = path_to_local(cast_expr) && let Some(span) = cx.tcx.hir().opt_span(id) - && span.ctxt() != cast_expr.span.ctxt() + && !span.eq_ctxt(cast_expr.span) { // Binding context is different than the identifiers context. // Weird macro wizardry could be involved here. diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 43eb6a9b8386..788fe8287278 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -118,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { vis.visit_ty(impl_.self_ty); for target in &vis.found { - if item.span.ctxt() != target.span().ctxt() { + if !item.span.eq_ctxt(target.span()) { return; } diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index d68c5c4bac68..5288efd8df8c 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -225,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { _: LocalDefId, ) { if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_))) - || span.ctxt() != body.value.span.ctxt() + || !span.eq_ctxt(body.value.span) || in_external_macro(cx.sess(), span) { return; diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index 63e64a5b35d0..47c9438c588f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -67,7 +67,7 @@ pub(super) fn check<'tcx>( } } - if unwrap_arg.span.ctxt() != map_span.ctxt() { + if !unwrap_arg.span.eq_ctxt(map_span) { return; } From 0489fd05d8562a7ccaeb79d0566b45bc463a0853 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 6 Jan 2024 01:32:03 +0300 Subject: [PATCH 114/257] library: Fix warnings in rtstartup --- library/rtstartup/rsbegin.rs | 1 + library/rtstartup/rsend.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index 1df0c8970538..14bce2bbeee2 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -18,6 +18,7 @@ #![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] +#![allow(internal_features)] #[lang = "sized"] trait Sized {} diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs index d5aca80edf9e..714643c83866 100644 --- a/library/rtstartup/rsend.rs +++ b/library/rtstartup/rsend.rs @@ -5,6 +5,7 @@ #![feature(auto_traits)] #![crate_type = "rlib"] #![no_core] +#![allow(internal_features)] #[lang = "sized"] trait Sized {} From 0d70e588e6624db9ce001d32e9ce1148cfde8085 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 6 Jan 2024 01:40:56 +0300 Subject: [PATCH 115/257] library: Fix a symlink test failing on Windows --- library/std/src/fs/tests.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 12afdef2669a..5917bf8df029 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -936,8 +936,10 @@ fn read_link() { } // Check that readlink works with non-drive paths on Windows. let link = tmpdir.join("link_unc"); - check!(symlink_dir(r"\\localhost\c$\", &link)); - assert_eq!(check!(fs::read_link(&link)), Path::new(r"\\localhost\c$\")); + if got_symlink_permission(&tmpdir) { + check!(symlink_dir(r"\\localhost\c$\", &link)); + assert_eq!(check!(fs::read_link(&link)), Path::new(r"\\localhost\c$\")); + }; } let link = tmpdir.join("link"); if !got_symlink_permission(&tmpdir) { From 2c088f95202400084987ed874822b4969f678d20 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 22 Dec 2023 14:56:20 +0300 Subject: [PATCH 116/257] Disallow reference to `static mut` for expressions Add `E0796` error code. Add `static_mut_ref` lint. This is the idea for the 2024 edition. --- Cargo.lock | 1 + compiler/rustc_error_codes/src/error_codes.rs | 1 + .../src/error_codes/E0796.md | 22 +++++ compiler/rustc_hir_analysis/Cargo.toml | 1 + compiler/rustc_hir_analysis/messages.ftl | 14 +++ compiler/rustc_hir_analysis/src/check/errs.rs | 78 ++++++++++++++++ compiler/rustc_hir_analysis/src/check/mod.rs | 1 + compiler/rustc_hir_analysis/src/errors.rs | 91 +++++++++++++++++++ compiler/rustc_lint_defs/src/builtin.rs | 52 +++++++++++ 9 files changed, 261 insertions(+) create mode 100644 compiler/rustc_error_codes/src/error_codes/E0796.md create mode 100644 compiler/rustc_hir_analysis/src/check/errs.rs diff --git a/Cargo.lock b/Cargo.lock index b8192e333fe9..1d6f7ea1e27c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3877,6 +3877,7 @@ dependencies = [ "rustc_feature", "rustc_fluent_macro", "rustc_hir", + "rustc_hir_pretty", "rustc_index", "rustc_infer", "rustc_lint_defs", diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 1028d43f9c5b..a1391cceb712 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -515,6 +515,7 @@ E0792: include_str!("./error_codes/E0792.md"), E0793: include_str!("./error_codes/E0793.md"), E0794: include_str!("./error_codes/E0794.md"), E0795: include_str!("./error_codes/E0795.md"), +E0796: include_str!("./error_codes/E0796.md"), } // Undocumented removed error codes. Note that many removed error codes are kept in the list above diff --git a/compiler/rustc_error_codes/src/error_codes/E0796.md b/compiler/rustc_error_codes/src/error_codes/E0796.md new file mode 100644 index 000000000000..cea18f8db851 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0796.md @@ -0,0 +1,22 @@ +Reference of mutable static. + +Erroneous code example: + +```compile_fail,edition2024,E0796 +static mut X: i32 = 23; +static mut Y: i32 = 24; + +unsafe { + let y = &X; + let ref x = X; + let (x, y) = (&X, &Y); + foo(&X); +} + +fn foo<'a>(_x: &'a i32) {} +``` + +Mutable statics can be written to by multiple threads: aliasing violations or +data races will cause undefined behavior. + +Reference of mutable static is a hard error from 2024 edition. diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index b671bebeb050..b5ebc1fab765 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -17,6 +17,7 @@ rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } rustc_lint_defs = { path = "../rustc_lint_defs" } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d8b6b9a1272f..6a17668ad17f 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -346,6 +346,20 @@ hir_analysis_start_not_target_feature = `#[start]` function is not allowed to ha hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]` .label = `#[start]` function is not allowed to be `#[track_caller]` +hir_analysis_static_mut_ref = reference of mutable static is disallowed + .label = reference of mutable static + .note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + .suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + .suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + +hir_analysis_static_mut_ref_lint = {$shared}reference of mutable static is discouraged + .label = shared reference of mutable static + .label_mut = mutable reference of mutable static + .suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + .suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + .note = reference of mutable static is a hard error from 2024 edition + .why_note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + hir_analysis_static_specialize = cannot specialize on `'static` lifetime hir_analysis_substs_on_overridden_impl = could not resolve substs on overridden impl diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs new file mode 100644 index 000000000000..5862643c42f8 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check/errs.rs @@ -0,0 +1,78 @@ +use rustc_hir as hir; +use rustc_hir_pretty::qpath_to_string; +use rustc_lint_defs::builtin::STATIC_MUT_REF; +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; +use rustc_type_ir::Mutability; + +use crate::errors; + +/// Check for shared or mutable references of `static mut` inside expression +pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) { + let span = expr.span; + let hir_id = expr.hir_id; + if let hir::ExprKind::AddrOf(borrow_kind, m, expr) = expr.kind + && matches!(borrow_kind, hir::BorrowKind::Ref) + && let Some(var) = is_path_static_mut(*expr) + { + handle_static_mut_ref( + tcx, + span, + var, + span.edition().at_least_rust_2024(), + matches!(m, Mutability::Mut), + hir_id, + ); + } +} + +fn is_path_static_mut(expr: hir::Expr<'_>) -> Option { + if let hir::ExprKind::Path(qpath) = expr.kind + && let hir::QPath::Resolved(_, path) = qpath + && let hir::def::Res::Def(def_kind, _) = path.res + && let hir::def::DefKind::Static(mt) = def_kind + && matches!(mt, Mutability::Mut) + { + return Some(qpath_to_string(&qpath)); + } + None +} + +fn handle_static_mut_ref( + tcx: TyCtxt<'_>, + span: Span, + var: String, + e2024: bool, + mutable: bool, + hir_id: hir::HirId, +) { + if e2024 { + let sugg = if mutable { + errors::StaticMutRefSugg::Mut { span, var } + } else { + errors::StaticMutRefSugg::Shared { span, var } + }; + tcx.sess.parse_sess.dcx.emit_err(errors::StaticMutRef { span, sugg }); + return; + } + + let (label, sugg, shared) = if mutable { + ( + errors::RefOfMutStaticLabel::Mut { span }, + errors::RefOfMutStaticSugg::Mut { span, var }, + "mutable ", + ) + } else { + ( + errors::RefOfMutStaticLabel::Shared { span }, + errors::RefOfMutStaticSugg::Shared { span, var }, + "shared ", + ) + }; + tcx.emit_spanned_lint( + STATIC_MUT_REF, + hir_id, + span, + errors::RefOfMutStatic { shared, why_note: (), label, sugg }, + ); +} diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index f60d6950670d..ac0c715c6b30 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -66,6 +66,7 @@ mod check; mod compare_impl_item; pub mod dropck; mod entry; +mod errs; pub mod intrinsic; pub mod intrinsicck; mod region; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 9124d5021105..4f22da4ba3b9 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1410,3 +1410,94 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> { pub mut_key: &'a str, pub ptr_ty: Ty<'a>, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_static_mut_ref, code = "E0796")] +#[note] +pub struct StaticMutRef { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub sugg: StaticMutRefSugg, +} + +#[derive(Subdiagnostic)] +pub enum StaticMutRefSugg { + #[suggestion( + hir_analysis_suggestion, + style = "verbose", + code = "addr_of!({var})", + applicability = "maybe-incorrect" + )] + Shared { + #[primary_span] + span: Span, + var: String, + }, + #[suggestion( + hir_analysis_suggestion_mut, + style = "verbose", + code = "addr_of_mut!({var})", + applicability = "maybe-incorrect" + )] + Mut { + #[primary_span] + span: Span, + var: String, + }, +} + +// STATIC_MUT_REF lint +#[derive(LintDiagnostic)] +#[diag(hir_analysis_static_mut_ref_lint)] +#[note] +pub struct RefOfMutStatic<'a> { + pub shared: &'a str, + #[note(hir_analysis_why_note)] + pub why_note: (), + #[subdiagnostic] + pub label: RefOfMutStaticLabel, + #[subdiagnostic] + pub sugg: RefOfMutStaticSugg, +} + +#[derive(Subdiagnostic)] +pub enum RefOfMutStaticLabel { + #[label(hir_analysis_label)] + Shared { + #[primary_span] + span: Span, + }, + #[label(hir_analysis_label_mut)] + Mut { + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +pub enum RefOfMutStaticSugg { + #[suggestion( + hir_analysis_suggestion, + style = "verbose", + code = "addr_of!({var})", + applicability = "maybe-incorrect" + )] + Shared { + #[primary_span] + span: Span, + var: String, + }, + #[suggestion( + hir_analysis_suggestion_mut, + style = "verbose", + code = "addr_of_mut!({var})", + applicability = "maybe-incorrect" + )] + Mut { + #[primary_span] + span: Span, + var: String, + }, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 19da51b7dcfa..cfba6780a66f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -89,6 +89,7 @@ declare_lint_pass! { SINGLE_USE_LIFETIMES, SOFT_UNSTABLE, STABLE_FEATURES, + STATIC_MUT_REF, SUSPICIOUS_AUTO_TRAIT_IMPLS, TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, @@ -1767,6 +1768,57 @@ declare_lint! { }; } +declare_lint! { + /// The `static_mut_ref` lint checks for shared or mutable references + /// of mutable static inside `unsafe` blocks and `unsafe` functions. + /// + /// ### Example + /// + /// ```rust,edition2021 + /// fn main() { + /// static mut X: i32 = 23; + /// static mut Y: i32 = 24; + /// + /// unsafe { + /// let y = &X; + /// let ref x = X; + /// let (x, y) = (&X, &Y); + /// foo(&X); + /// } + /// } + /// + /// unsafe fn _foo() { + /// static mut X: i32 = 23; + /// static mut Y: i32 = 24; + /// + /// let y = &X; + /// let ref x = X; + /// let (x, y) = (&X, &Y); + /// foo(&X); + /// } + /// + /// fn foo<'a>(_x: &'a i32) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Shared or mutable references of mutable static are almost always a mistake and + /// can lead to undefined behavior and various other problems in your code. + /// + /// This lint is "warn" by default on editions up to 2021, from 2024 there is + /// a hard error instead. + pub STATIC_MUT_REF, + Warn, + "shared references or mutable references of mutable static is discouraged", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #114447 ", + explain_reason: false, + }; +} + declare_lint! { /// The `absolute_paths_not_starting_with_crate` lint detects fully /// qualified paths that start with a module name instead of `crate`, From 70ba4d14b3d1f81d27ad7b7bab81aac19e412c20 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 22 Dec 2023 15:00:25 +0300 Subject: [PATCH 117/257] Disallow reference to `static mut` for statements --- compiler/rustc_hir_analysis/src/check/errs.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs index 5862643c42f8..27bb2c57a5cb 100644 --- a/compiler/rustc_hir_analysis/src/check/errs.rs +++ b/compiler/rustc_hir_analysis/src/check/errs.rs @@ -26,6 +26,25 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) { } } +/// Check for shared or mutable references of `static mut` inside statement +pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) { + if let hir::StmtKind::Local(loc) = stmt.kind + && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind + && matches!(ba.0, rustc_ast::ByRef::Yes) + && let Some(init) = loc.init + && let Some(var) = is_path_static_mut(*init) + { + handle_static_mut_ref( + tcx, + init.span, + var, + loc.span.edition().at_least_rust_2024(), + matches!(ba.1, Mutability::Mut), + stmt.hir_id, + ); + } +} + fn is_path_static_mut(expr: hir::Expr<'_>) -> Option { if let hir::ExprKind::Path(qpath) = expr.kind && let hir::QPath::Resolved(_, path) = qpath From a82fd2bc7cf15b8aa9ffbd7752999a3bc9cb8cea Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 22 Dec 2023 15:03:45 +0300 Subject: [PATCH 118/257] Call `maybe_expr_static_mut` inside `resolve_expr` --- compiler/rustc_hir_analysis/src/check/region.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 542e69a6c34d..1abdc7b21df9 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -18,6 +18,8 @@ use rustc_middle::ty::TyCtxt; use rustc_span::source_map; use rustc_span::Span; +use super::errs::maybe_expr_static_mut; + use std::mem; #[derive(Debug, Copy, Clone)] @@ -242,6 +244,8 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) { debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr); + maybe_expr_static_mut(visitor.tcx, *expr); + let prev_cx = visitor.cx; visitor.enter_node_scope_with_dtor(expr.hir_id.local_id); From e36f7b7a5cacd9bfac9e7679ce6de26c03eb5795 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 22 Dec 2023 15:09:10 +0300 Subject: [PATCH 119/257] Call `maybe_stmt_static_mut` inside `resolve_stmt` --- compiler/rustc_hir_analysis/src/check/region.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 1abdc7b21df9..5d5a4789734a 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::source_map; use rustc_span::Span; -use super::errs::maybe_expr_static_mut; +use super::errs::{maybe_expr_static_mut, maybe_stmt_static_mut}; use std::mem; @@ -226,6 +226,8 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h let stmt_id = stmt.hir_id.local_id; debug!("resolve_stmt(stmt.id={:?})", stmt_id); + maybe_stmt_static_mut(visitor.tcx, *stmt); + // Every statement will clean up the temporaries created during // execution of that statement. Therefore each statement has an // associated destruction scope that represents the scope of the From 18edf9a64e8d84e72a6be67df3822e57dea8d34a Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 22 Dec 2023 15:10:47 +0300 Subject: [PATCH 120/257] Add test for `E0796` and `static_mut_ref` lint --- .../reference-of-mut-static-safe.e2021.stderr | 26 ++++++ .../reference-of-mut-static-safe.e2024.stderr | 15 +++ .../ui/static/reference-of-mut-static-safe.rs | 13 +++ .../reference-of-mut-static-unsafe-fn.rs | 23 +++++ .../reference-of-mut-static-unsafe-fn.stderr | 63 +++++++++++++ .../reference-of-mut-static.e2021.stderr | 91 +++++++++++++++++++ .../reference-of-mut-static.e2024.stderr | 75 +++++++++++++++ tests/ui/static/reference-of-mut-static.rs | 50 ++++++++++ 8 files changed, 356 insertions(+) create mode 100644 tests/ui/static/reference-of-mut-static-safe.e2021.stderr create mode 100644 tests/ui/static/reference-of-mut-static-safe.e2024.stderr create mode 100644 tests/ui/static/reference-of-mut-static-safe.rs create mode 100644 tests/ui/static/reference-of-mut-static-unsafe-fn.rs create mode 100644 tests/ui/static/reference-of-mut-static-unsafe-fn.stderr create mode 100644 tests/ui/static/reference-of-mut-static.e2021.stderr create mode 100644 tests/ui/static/reference-of-mut-static.e2024.stderr create mode 100644 tests/ui/static/reference-of-mut-static.rs diff --git a/tests/ui/static/reference-of-mut-static-safe.e2021.stderr b/tests/ui/static/reference-of-mut-static-safe.e2021.stderr new file mode 100644 index 000000000000..c38fe8790637 --- /dev/null +++ b/tests/ui/static/reference-of-mut-static-safe.e2021.stderr @@ -0,0 +1,26 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/reference-of-mut-static-safe.rs:9:14 + | +LL | let _x = &X; + | ^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let _x = addr_of!(X); + | ~~~~~~~~~~~ + +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/reference-of-mut-static-safe.rs:9:14 + | +LL | let _x = &X; + | ^^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/static/reference-of-mut-static-safe.e2024.stderr b/tests/ui/static/reference-of-mut-static-safe.e2024.stderr new file mode 100644 index 000000000000..53f81179de55 --- /dev/null +++ b/tests/ui/static/reference-of-mut-static-safe.e2024.stderr @@ -0,0 +1,15 @@ +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static-safe.rs:9:14 + | +LL | let _x = &X; + | ^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let _x = addr_of!(X); + | ~~~~~~~~~~~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0796`. diff --git a/tests/ui/static/reference-of-mut-static-safe.rs b/tests/ui/static/reference-of-mut-static-safe.rs new file mode 100644 index 000000000000..5cb1a03bef51 --- /dev/null +++ b/tests/ui/static/reference-of-mut-static-safe.rs @@ -0,0 +1,13 @@ +// revisions: e2021 e2024 + +// [e2021] edition:2021 +// [e2024] compile-flags: --edition 2024 -Z unstable-options + +fn main() { + static mut X: i32 = 1; + + let _x = &X; + //[e2024]~^ reference of mutable static is disallowed [E0796] + //[e2021]~^^ use of mutable static is unsafe and requires unsafe function or block [E0133] + //[e2021]~^^^ shared reference of mutable static is discouraged [static_mut_ref] +} diff --git a/tests/ui/static/reference-of-mut-static-unsafe-fn.rs b/tests/ui/static/reference-of-mut-static-unsafe-fn.rs new file mode 100644 index 000000000000..6b1e77850e50 --- /dev/null +++ b/tests/ui/static/reference-of-mut-static-unsafe-fn.rs @@ -0,0 +1,23 @@ +// compile-flags: --edition 2024 -Z unstable-options + +fn main() {} + +unsafe fn _foo() { + static mut X: i32 = 1; + static mut Y: i32 = 1; + + let _y = &X; + //~^ ERROR reference of mutable static is disallowed + + let ref _a = X; + //~^ ERROR reference of mutable static is disallowed + + let (_b, _c) = (&X, &Y); + //~^ ERROR reference of mutable static is disallowed + //~^^ ERROR reference of mutable static is disallowed + + foo(&X); + //~^ ERROR reference of mutable static is disallowed +} + +fn foo<'a>(_x: &'a i32) {} diff --git a/tests/ui/static/reference-of-mut-static-unsafe-fn.stderr b/tests/ui/static/reference-of-mut-static-unsafe-fn.stderr new file mode 100644 index 000000000000..5c6fdedfa96f --- /dev/null +++ b/tests/ui/static/reference-of-mut-static-unsafe-fn.stderr @@ -0,0 +1,63 @@ +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static-unsafe-fn.rs:9:14 + | +LL | let _y = &X; + | ^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let _y = addr_of!(X); + | ~~~~~~~~~~~ + +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static-unsafe-fn.rs:12:18 + | +LL | let ref _a = X; + | ^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let ref _a = addr_of!(X); + | ~~~~~~~~~~~ + +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static-unsafe-fn.rs:15:21 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let (_b, _c) = (addr_of!(X), &Y); + | ~~~~~~~~~~~ + +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static-unsafe-fn.rs:15:25 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let (_b, _c) = (&X, addr_of!(Y)); + | ~~~~~~~~~~~ + +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static-unsafe-fn.rs:19:9 + | +LL | foo(&X); + | ^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | foo(addr_of!(X)); + | ~~~~~~~~~~~ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0796`. diff --git a/tests/ui/static/reference-of-mut-static.e2021.stderr b/tests/ui/static/reference-of-mut-static.e2021.stderr new file mode 100644 index 000000000000..77a6b3d304bd --- /dev/null +++ b/tests/ui/static/reference-of-mut-static.e2021.stderr @@ -0,0 +1,91 @@ +error: shared reference of mutable static is discouraged + --> $DIR/reference-of-mut-static.rs:16:18 + | +LL | let _y = &X; + | ^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +note: the lint level is defined here + --> $DIR/reference-of-mut-static.rs:6:9 + | +LL | #![deny(static_mut_ref)] + | ^^^^^^^^^^^^^^ +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let _y = addr_of!(X); + | ~~~~~~~~~~~ + +error: mutable reference of mutable static is discouraged + --> $DIR/reference-of-mut-static.rs:20:18 + | +LL | let _y = &mut X; + | ^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | let _y = addr_of_mut!(X); + | ~~~~~~~~~~~~~~~ + +error: shared reference of mutable static is discouraged + --> $DIR/reference-of-mut-static.rs:28:22 + | +LL | let ref _a = X; + | ^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let ref _a = addr_of!(X); + | ~~~~~~~~~~~ + +error: shared reference of mutable static is discouraged + --> $DIR/reference-of-mut-static.rs:32:25 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let (_b, _c) = (addr_of!(X), &Y); + | ~~~~~~~~~~~ + +error: shared reference of mutable static is discouraged + --> $DIR/reference-of-mut-static.rs:32:29 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let (_b, _c) = (&X, addr_of!(Y)); + | ~~~~~~~~~~~ + +error: shared reference of mutable static is discouraged + --> $DIR/reference-of-mut-static.rs:38:13 + | +LL | foo(&X); + | ^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | foo(addr_of!(X)); + | ~~~~~~~~~~~ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/static/reference-of-mut-static.e2024.stderr b/tests/ui/static/reference-of-mut-static.e2024.stderr new file mode 100644 index 000000000000..f445ec65a5d2 --- /dev/null +++ b/tests/ui/static/reference-of-mut-static.e2024.stderr @@ -0,0 +1,75 @@ +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static.rs:16:18 + | +LL | let _y = &X; + | ^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let _y = addr_of!(X); + | ~~~~~~~~~~~ + +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static.rs:20:18 + | +LL | let _y = &mut X; + | ^^^^^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | let _y = addr_of_mut!(X); + | ~~~~~~~~~~~~~~~ + +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static.rs:28:22 + | +LL | let ref _a = X; + | ^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let ref _a = addr_of!(X); + | ~~~~~~~~~~~ + +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static.rs:32:25 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let (_b, _c) = (addr_of!(X), &Y); + | ~~~~~~~~~~~ + +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static.rs:32:29 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let (_b, _c) = (&X, addr_of!(Y)); + | ~~~~~~~~~~~ + +error[E0796]: reference of mutable static is disallowed + --> $DIR/reference-of-mut-static.rs:38:13 + | +LL | foo(&X); + | ^^ reference of mutable static + | + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | foo(addr_of!(X)); + | ~~~~~~~~~~~ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0796`. diff --git a/tests/ui/static/reference-of-mut-static.rs b/tests/ui/static/reference-of-mut-static.rs new file mode 100644 index 000000000000..01a3b1fbd9b5 --- /dev/null +++ b/tests/ui/static/reference-of-mut-static.rs @@ -0,0 +1,50 @@ +// revisions: e2021 e2024 + +// [e2021] edition:2021 +// [e2024] compile-flags: --edition 2024 -Z unstable-options + +#![deny(static_mut_ref)] + +use std::ptr::{addr_of, addr_of_mut}; + +fn main() { + static mut X: i32 = 1; + + static mut Y: i32 = 1; + + unsafe { + let _y = &X; + //[e2024]~^ ERROR reference of mutable static is disallowed + //[e2021]~^^ ERROR shared reference of mutable static is discouraged [static_mut_ref] + + let _y = &mut X; + //[e2024]~^ ERROR reference of mutable static is disallowed + //[e2021]~^^ ERROR mutable reference of mutable static is discouraged [static_mut_ref] + + let _z = addr_of_mut!(X); + + let _p = addr_of!(X); + + let ref _a = X; + //[e2024]~^ ERROR reference of mutable static is disallowed + //[e2021]~^^ ERROR shared reference of mutable static is discouraged [static_mut_ref] + + let (_b, _c) = (&X, &Y); + //[e2024]~^ ERROR reference of mutable static is disallowed + //[e2021]~^^ ERROR shared reference of mutable static is discouraged [static_mut_ref] + //[e2024]~^^^ ERROR reference of mutable static is disallowed + //[e2021]~^^^^ ERROR shared reference of mutable static is discouraged [static_mut_ref] + + foo(&X); + //[e2024]~^ ERROR reference of mutable static is disallowed + //[e2021]~^^ ERROR shared reference of mutable static is discouraged [static_mut_ref] + + static mut Z: &[i32; 3] = &[0, 1, 2]; + + let _ = Z.len(); + let _ = Z[0]; + let _ = format!("{:?}", Z); + } +} + +fn foo<'a>(_x: &'a i32) {} From d0e81a12f542892de50c9f72b9d1563cba9148f3 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 5 Jan 2024 23:11:26 -0500 Subject: [PATCH 121/257] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index add15366eaf3..2ce45605d9db 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit add15366eaf3f3eb84717d3b8b71902ca36a7c84 +Subproject commit 2ce45605d9db521b5fd6c1211ce8de6055fdb24e From d70f0e36f08f217bb2fd2814b0d9b116c2d2ccbe Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 6 Jan 2024 10:06:15 +0000 Subject: [PATCH 122/257] compiler: update Fuchsia sanitizer support. --- .../rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs index f0515615b195..5b19ed1b5ff1 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs @@ -6,7 +6,8 @@ pub fn target() -> Target { base.plt_by_default = false; base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI; + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-fuchsia".into(), From 099b15f4fc7620a9fe389f2d9aa239d563762632 Mon Sep 17 00:00:00 2001 From: Connor Lane Smith Date: Sat, 6 Jan 2024 11:51:39 +0000 Subject: [PATCH 123/257] Fix typo in docs for slice::split_once, slice::rsplit_once --- library/core/src/slice/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index b14d9712794b..5edc89e4cb53 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2483,7 +2483,7 @@ impl [T] { /// Splits the slice on the first element that matches the specified /// predicate. /// - /// If any matching elements are resent in the slice, returns the prefix + /// If any matching elements are present in the slice, returns the prefix /// before the match and suffix after. The matching element itself is not /// included. If no elements match, returns `None`. /// @@ -2511,7 +2511,7 @@ impl [T] { /// Splits the slice on the last element that matches the specified /// predicate. /// - /// If any matching elements are resent in the slice, returns the prefix + /// If any matching elements are present in the slice, returns the prefix /// before the match and suffix after. The matching element itself is not /// included. If no elements match, returns `None`. /// From 5c743298879f9e06ebcf3286dad2a10d77cc162c Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 6 Jan 2024 12:53:06 +0100 Subject: [PATCH 124/257] Remove ignore-stage1 that was added when changing error count msg The bootstrap bump has happened, so the bootstrap compiler now contains the new diagnotic. --- tests/ui-fulldeps/dropck-tarena-cycle-checked.rs | 2 -- tests/ui-fulldeps/dropck-tarena-cycle-checked.stderr | 2 +- tests/ui-fulldeps/dropck-tarena-unsound-drop.rs | 2 -- tests/ui-fulldeps/dropck-tarena-unsound-drop.stderr | 2 +- tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs | 1 - tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr | 4 ++-- tests/ui-fulldeps/pathless-extern-unstable.rs | 1 - tests/ui-fulldeps/pathless-extern-unstable.stderr | 2 +- tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs | 1 - .../ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr | 2 +- 10 files changed, 6 insertions(+), 13 deletions(-) diff --git a/tests/ui-fulldeps/dropck-tarena-cycle-checked.rs b/tests/ui-fulldeps/dropck-tarena-cycle-checked.rs index 188da67295a1..cc97971a0ddb 100644 --- a/tests/ui-fulldeps/dropck-tarena-cycle-checked.rs +++ b/tests/ui-fulldeps/dropck-tarena-cycle-checked.rs @@ -1,5 +1,3 @@ -// ignore-stage1 - // Reject mixing cyclic structure and Drop when using TypedArena. // // (Compare against dropck-vec-cycle-checked.rs) diff --git a/tests/ui-fulldeps/dropck-tarena-cycle-checked.stderr b/tests/ui-fulldeps/dropck-tarena-cycle-checked.stderr index 2f5be3f7f559..f9ac36be60e7 100644 --- a/tests/ui-fulldeps/dropck-tarena-cycle-checked.stderr +++ b/tests/ui-fulldeps/dropck-tarena-cycle-checked.stderr @@ -1,5 +1,5 @@ error[E0597]: `arena` does not live long enough - --> $DIR/dropck-tarena-cycle-checked.rs:118:7 + --> $DIR/dropck-tarena-cycle-checked.rs:116:7 | LL | let arena = TypedArena::default(); | ----- binding `arena` declared here diff --git a/tests/ui-fulldeps/dropck-tarena-unsound-drop.rs b/tests/ui-fulldeps/dropck-tarena-unsound-drop.rs index 5f9a5fb76eb4..86485a9887fb 100644 --- a/tests/ui-fulldeps/dropck-tarena-unsound-drop.rs +++ b/tests/ui-fulldeps/dropck-tarena-unsound-drop.rs @@ -1,5 +1,3 @@ -// ignore-stage1 - // Check that an arena (TypedArena) cannot carry elements whose drop // methods might access borrowed data of lifetime that does not // strictly outlive the arena itself. diff --git a/tests/ui-fulldeps/dropck-tarena-unsound-drop.stderr b/tests/ui-fulldeps/dropck-tarena-unsound-drop.stderr index 8bb3911241d1..82aa339a83b9 100644 --- a/tests/ui-fulldeps/dropck-tarena-unsound-drop.stderr +++ b/tests/ui-fulldeps/dropck-tarena-unsound-drop.stderr @@ -1,5 +1,5 @@ error[E0597]: `arena` does not live long enough - --> $DIR/dropck-tarena-unsound-drop.rs:43:7 + --> $DIR/dropck-tarena-unsound-drop.rs:41:7 | LL | let arena: TypedArena = TypedArena::default(); | ----- binding `arena` declared here diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs index 392199866654..39980ee7c672 100644 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs +++ b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs @@ -1,6 +1,5 @@ // Test the `rustc::span_use_eq_ctxt` internal lint // compile-flags: -Z unstable-options -// ignore-stage1 #![feature(rustc_private)] #![deny(rustc::span_use_eq_ctxt)] diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr index fcf2565c8ab1..b52df0368b16 100644 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr +++ b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr @@ -1,11 +1,11 @@ error: use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` - --> $DIR/span_use_eq_ctxt.rs:13:5 + --> $DIR/span_use_eq_ctxt.rs:12:5 | LL | s.ctxt() == t.ctxt() | ^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/span_use_eq_ctxt.rs:6:9 + --> $DIR/span_use_eq_ctxt.rs:5:9 | LL | #![deny(rustc::span_use_eq_ctxt)] | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-fulldeps/pathless-extern-unstable.rs b/tests/ui-fulldeps/pathless-extern-unstable.rs index 50d157c5795e..7fba8343bc08 100644 --- a/tests/ui-fulldeps/pathless-extern-unstable.rs +++ b/tests/ui-fulldeps/pathless-extern-unstable.rs @@ -1,6 +1,5 @@ // edition:2018 // compile-flags:--extern rustc_middle -// ignore-stage1 // Test that `--extern rustc_middle` fails with `rustc_private`. diff --git a/tests/ui-fulldeps/pathless-extern-unstable.stderr b/tests/ui-fulldeps/pathless-extern-unstable.stderr index 840b95d755c6..cfd8669c45fe 100644 --- a/tests/ui-fulldeps/pathless-extern-unstable.stderr +++ b/tests/ui-fulldeps/pathless-extern-unstable.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? - --> $DIR/pathless-extern-unstable.rs:7:9 + --> $DIR/pathless-extern-unstable.rs:6:9 | LL | pub use rustc_middle; | ^^^^^^^^^^^^ diff --git a/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs b/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs index 331483112611..a0a8114e0c5c 100644 --- a/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs +++ b/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs @@ -1,5 +1,4 @@ // rustc-env:CARGO_CRATE_NAME=rustc_dummy -// ignore-stage1 #![feature(rustc_private)] #![crate_type = "lib"] diff --git a/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr b/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr index c752a5ee0575..4cdc24e6a6bc 100644 --- a/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr +++ b/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr @@ -1,5 +1,5 @@ error: diagnostic slug and crate name do not match - --> $DIR/enforce_slug_naming.rs:23:8 + --> $DIR/enforce_slug_naming.rs:22:8 | LL | #[diag(compiletest_example, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^ From 96acaff359d489a55602f444fa6b5f5d30912fee Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jan 2024 12:57:03 +0100 Subject: [PATCH 125/257] document rounding behavior of rint/nearbyint for ties --- library/core/src/intrinsics.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 696bf6f3a946..3d5b544bc1bc 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1783,27 +1783,33 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn truncf64(x: f64) -> f64; - /// Returns the nearest integer to an `f32`. May raise an inexact floating-point exception - /// if the argument is not an integer. + /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. /// /// The stabilized version of this intrinsic is /// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even) #[rustc_nounwind] pub fn rintf32(x: f32) -> f32; - /// Returns the nearest integer to an `f64`. May raise an inexact floating-point exception - /// if the argument is not an integer. + /// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. /// /// The stabilized version of this intrinsic is /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) #[rustc_nounwind] pub fn rintf64(x: f64) -> f64; - /// Returns the nearest integer to an `f32`. + /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. /// /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn nearbyintf32(x: f32) -> f32; - /// Returns the nearest integer to an `f64`. + /// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. /// /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] From 56173611d65bae5eacef80d15799ba89161cd38b Mon Sep 17 00:00:00 2001 From: klensy Date: Tue, 2 Jan 2024 22:36:01 +0300 Subject: [PATCH 126/257] don't reexport atomic::ordering via rustc_data_structures, use std import --- compiler/rustc_data_structures/src/sync.rs | 7 ++----- compiler/rustc_driver_impl/src/lib.rs | 3 +-- compiler/rustc_query_impl/src/plumbing.rs | 2 +- compiler/rustc_query_system/src/dep_graph/graph.rs | 14 +++++++------- compiler/rustc_session/src/session.rs | 6 ++---- 5 files changed, 13 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 43221d70e21c..48edfba8da08 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -56,9 +56,6 @@ mod parallel; pub use parallel::scope; pub use parallel::{join, par_for_each_in, par_map, parallel_guard, try_par_for_each_in}; -pub use std::sync::atomic::Ordering; -pub use std::sync::atomic::Ordering::SeqCst; - pub use vec::{AppendOnlyIndexVec, AppendOnlyVec}; mod vec; @@ -67,8 +64,7 @@ mod freeze; pub use freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard}; mod mode { - use super::Ordering; - use std::sync::atomic::AtomicU8; + use std::sync::atomic::{AtomicU8, Ordering}; const UNINITIALIZED: u8 = 0; const DYN_NOT_THREAD_SAFE: u8 = 1; @@ -113,6 +109,7 @@ cfg_match! { cfg(not(parallel_compiler)) => { use std::ops::Add; use std::cell::Cell; + use std::sync::atomic::Ordering; pub unsafe auto trait Send {} pub unsafe auto trait Sync {} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index fd925b62702a..3add7e3c643e 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -25,7 +25,6 @@ use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults}; use rustc_data_structures::profiling::{ get_resident_set_size, print_time_passes_entry, TimePassesFormat, }; -use rustc_data_structures::sync::SeqCst; use rustc_errors::registry::{InvalidErrorCode, Registry}; use rustc_errors::{markdown, ColorConfig}; use rustc_errors::{DiagCtxt, ErrorGuaranteed, PResult}; @@ -476,7 +475,7 @@ fn run_compiler( eprintln!( "Fuel used by {}: {}", sess.opts.unstable_opts.print_fuel.as_ref().unwrap(), - sess.print_fuel.load(SeqCst) + sess.print_fuel.load(Ordering::SeqCst) ); } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index f131a0f75932..fd7f1669ba45 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -69,7 +69,7 @@ impl QueryContext for QueryCtxt<'_> { fn next_job_id(self) -> QueryJobId { QueryJobId( NonZeroU64::new( - self.query_system.jobs.fetch_add(1, rustc_data_structures::sync::Ordering::Relaxed), + self.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed), ) .unwrap(), ) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 9b06823dfbaf..1f09de0ed70c 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -4,7 +4,7 @@ use rustc_data_structures::profiling::{EventId, QueryInvocationId, SelfProfilerR use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering}; +use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc}; use rustc_data_structures::unord::UnordMap; use rustc_index::IndexVec; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; @@ -13,7 +13,7 @@ use std::collections::hash_map::Entry; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; -use std::sync::atomic::Ordering::Relaxed; +use std::sync::atomic::Ordering; use super::query::DepGraphQuery; use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex}; @@ -476,7 +476,7 @@ impl DepGraph { let task_deps = &mut *task_deps; if cfg!(debug_assertions) { - data.current.total_read_count.fetch_add(1, Relaxed); + data.current.total_read_count.fetch_add(1, Ordering::Relaxed); } // As long as we only have a low number of reads we can avoid doing a hash @@ -506,7 +506,7 @@ impl DepGraph { } } } else if cfg!(debug_assertions) { - data.current.total_duplicate_read_count.fetch_add(1, Relaxed); + data.current.total_duplicate_read_count.fetch_add(1, Ordering::Relaxed); } }) } @@ -976,8 +976,8 @@ impl DepGraph { pub fn print_incremental_info(&self) { if let Some(data) = &self.data { data.current.encoder.borrow().print_incremental_info( - data.current.total_read_count.load(Relaxed), - data.current.total_duplicate_read_count.load(Relaxed), + data.current.total_read_count.load(Ordering::Relaxed), + data.current.total_duplicate_read_count.load(Ordering::Relaxed), ) } } @@ -992,7 +992,7 @@ impl DepGraph { pub(crate) fn next_virtual_depnode_index(&self) -> DepNodeIndex { debug_assert!(self.data.is_none()); - let index = self.virtual_dep_node_index.fetch_add(1, Relaxed); + let index = self.virtual_dep_node_index.fetch_add(1, Ordering::Relaxed); DepNodeIndex::from_u32(index) } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ce166ae352fd..0ada48986349 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -14,9 +14,7 @@ use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::jobserver::{self, Client}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; -use rustc_data_structures::sync::{ - AtomicU64, DynSend, DynSync, Lock, Lrc, OneThread, Ordering::SeqCst, -}; +use rustc_data_structures::sync::{AtomicU64, DynSend, DynSync, Lock, Lrc, OneThread}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::emitter::{DynEmitter, HumanEmitter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; @@ -44,7 +42,7 @@ use std::fmt; use std::ops::{Div, Mul}; use std::path::{Path, PathBuf}; use std::str::FromStr; -use std::sync::{atomic::AtomicBool, Arc}; +use std::sync::{atomic::AtomicBool, atomic::Ordering::SeqCst, Arc}; struct OptimizationFuel { /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`. From 42921900a310a8792701b1fd9c0ba961607dc276 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jan 2024 14:54:08 +0100 Subject: [PATCH 127/257] remove an unnecessary stderr-per-bitwidth --- .../ui/consts/const-eval/ub-enum.64bit.stderr | 134 ------------------ tests/ui/consts/const-eval/ub-enum.rs | 4 +- .../{ub-enum.32bit.stderr => ub-enum.stderr} | 4 +- tests/ui/consts/const-eval/ub-uninhabit.rs | 2 +- tests/ui/consts/validate_never_arrays.rs | 2 +- 5 files changed, 6 insertions(+), 140 deletions(-) delete mode 100644 tests/ui/consts/const-eval/ub-enum.64bit.stderr rename tests/ui/consts/const-eval/{ub-enum.32bit.stderr => ub-enum.stderr} (98%) diff --git a/tests/ui/consts/const-eval/ub-enum.64bit.stderr b/tests/ui/consts/const-eval/ub-enum.64bit.stderr deleted file mode 100644 index 6db43d379d15..000000000000 --- a/tests/ui/consts/const-eval/ub-enum.64bit.stderr +++ /dev/null @@ -1,134 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:27:1 - | -LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0000000000000001, but expected a valid enum tag - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:30:1 - | -LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:33:1 - | -LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:45:1 - | -LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0000000000000000, but expected a valid enum tag - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:47:1 - | -LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:50:1 - | -LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:59:42 - | -LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:64:1 - | -LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:81:1 - | -LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:83:1 - | -LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:91:1 - | -LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0.1: encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:96:77 - | -LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:98:77 - | -LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant - -error[E0080]: evaluation of constant value failed - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL - | - = note: read discriminant of an uninhabited enum variant - | -note: inside `discriminant::` - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL -note: inside `TEST_ICE_89765` - --> $DIR/ub-enum.rs:103:14 - | -LL | unsafe { std::mem::discriminant(&*(&() as *const () as *const Never)); }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 14 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/ub-enum.rs b/tests/ui/consts/const-eval/ub-enum.rs index 72a0c9efed2c..c11ace612f13 100644 --- a/tests/ui/consts/const-eval/ub-enum.rs +++ b/tests/ui/consts/const-eval/ub-enum.rs @@ -1,7 +1,7 @@ -// stderr-per-bitwidth // Strip out raw byte dumps to make comparison platform-independent: // normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" -// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*a(lloc)?[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" +// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +// normalize-stderr-test "0x0+" -> "0x0" #![feature(never_type)] #![allow(invalid_value)] diff --git a/tests/ui/consts/const-eval/ub-enum.32bit.stderr b/tests/ui/consts/const-eval/ub-enum.stderr similarity index 98% rename from tests/ui/consts/const-eval/ub-enum.32bit.stderr rename to tests/ui/consts/const-eval/ub-enum.stderr index c0ad6caecf2b..a0712f64c7bc 100644 --- a/tests/ui/consts/const-eval/ub-enum.32bit.stderr +++ b/tests/ui/consts/const-eval/ub-enum.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:27:1 | LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x00000001, but expected a valid enum tag + | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x01, but expected a valid enum tag | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { @@ -31,7 +31,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:45:1 | LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x00000000, but expected a valid enum tag + | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0, but expected a valid enum tag | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { diff --git a/tests/ui/consts/const-eval/ub-uninhabit.rs b/tests/ui/consts/const-eval/ub-uninhabit.rs index 01600f545ae0..0eb9ab415d79 100644 --- a/tests/ui/consts/const-eval/ub-uninhabit.rs +++ b/tests/ui/consts/const-eval/ub-uninhabit.rs @@ -1,6 +1,6 @@ // Strip out raw byte dumps to make comparison platform-independent: // normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" -// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*a(lloc)?[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" +// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" #![feature(core_intrinsics)] #![feature(never_type)] diff --git a/tests/ui/consts/validate_never_arrays.rs b/tests/ui/consts/validate_never_arrays.rs index f96ca6839265..71c1340e5f81 100644 --- a/tests/ui/consts/validate_never_arrays.rs +++ b/tests/ui/consts/validate_never_arrays.rs @@ -1,6 +1,6 @@ // Strip out raw byte dumps to make comparison platform-independent: // normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" -// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*a(lloc)?[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" +// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" #![feature(never_type)] const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; //~ ERROR undefined behavior From 963568b46f2f0cf696c2d67441d167e1ce6cbc0a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 6 Jan 2024 15:04:58 +0100 Subject: [PATCH 128/257] feat: IDE features for primitive tuple fields --- Cargo.lock | 1 + crates/hir-def/src/lib.rs | 9 ++++ crates/hir-ty/Cargo.toml | 3 +- crates/hir-ty/src/infer.rs | 28 +++++++++++-- crates/hir-ty/src/infer/closure.rs | 39 ++++++++++------- crates/hir-ty/src/infer/expr.rs | 34 +++++++++------ crates/hir-ty/src/mir.rs | 21 ++++++---- crates/hir-ty/src/mir/borrowck.rs | 2 +- crates/hir-ty/src/mir/eval.rs | 12 ++++-- crates/hir-ty/src/mir/lower.rs | 42 ++++++++++++------- .../hir-ty/src/mir/lower/pattern_matching.rs | 19 ++++++--- crates/hir-ty/src/mir/pretty.rs | 9 +++- crates/hir/src/display.rs | 11 ++++- crates/hir/src/lib.rs | 25 ++++++++++- crates/hir/src/semantics.rs | 6 +-- crates/hir/src/source_analyzer.rs | 24 ++++++++--- crates/ide-db/src/defs.rs | 25 +++++++---- crates/ide-db/src/rename.rs | 1 + crates/ide/src/doc_links.rs | 2 + crates/ide/src/moniker.rs | 5 ++- crates/ide/src/navigation_target.rs | 2 +- .../ide/src/syntax_highlighting/highlight.rs | 12 ++++-- crates/ide/src/syntax_highlighting/inject.rs | 2 +- .../test_data/highlight_macros.html | 2 +- crates/ide/src/syntax_highlighting/tests.rs | 2 +- 25 files changed, 242 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7d110eafb66..6670e92f51bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -569,6 +569,7 @@ dependencies = [ "expect-test", "hir-def", "hir-expand", + "indexmap", "intern", "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 22ba3aab4e9f..250d7b677b5d 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -307,6 +307,15 @@ pub struct FieldId { pub type LocalFieldId = Idx; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct TupleId(pub u32); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct TupleFieldId { + pub tuple: TupleId, + pub index: u32, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ConstId(salsa::InternId); type ConstLoc = AssocItemLoc; diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 1873e7bfe6a5..c7807bcf9aa1 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -32,6 +32,7 @@ once_cell = "1.17.0" triomphe.workspace = true nohash-hasher.workspace = true typed-arena = "2.0.1" +indexmap.workspace = true rustc-dependencies.workspace = true @@ -60,4 +61,4 @@ test-fixture.workspace = true in-rust-tree = ["rustc-dependencies/in-rust-tree"] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 8053300ad220..63edd0f824f4 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -41,9 +41,10 @@ use hir_def::{ resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, type_ref::TypeRef, AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup, - TraitId, TypeAliasId, VariantId, + TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, }; use hir_expand::name::{name, Name}; +use indexmap::IndexSet; use la_arena::{ArenaMap, Entry}; use rustc_hash::{FxHashMap, FxHashSet}; use stdx::{always, never}; @@ -403,11 +404,15 @@ pub struct InferenceResult { /// For each method call expr, records the function it resolves to. method_resolutions: FxHashMap, /// For each field access expr, records the field it resolves to. - field_resolutions: FxHashMap, + field_resolutions: FxHashMap>, /// For each struct literal or pattern, records the variant it resolves to. variant_resolutions: FxHashMap, /// For each associated item record what it resolves to assoc_resolutions: FxHashMap, + /// Whenever a tuple field expression access a tuple field, we allocate a tuple id in + /// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of + /// that which allows us to resolve a [`TupleFieldId`]s type. + pub tuple_field_access_types: FxHashMap, pub diagnostics: Vec, pub type_of_expr: ArenaMap, /// For each pattern record the type it resolves to. @@ -447,7 +452,7 @@ impl InferenceResult { pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, Substitution)> { self.method_resolutions.get(&expr).cloned() } - pub fn field_resolution(&self, expr: ExprId) -> Option { + pub fn field_resolution(&self, expr: ExprId) -> Option> { self.field_resolutions.get(&expr).copied() } pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option { @@ -517,6 +522,8 @@ pub(crate) struct InferenceContext<'a> { /// The traits in scope, disregarding block modules. This is used for caching purposes. traits_in_scope: FxHashSet, pub(crate) result: InferenceResult, + tuple_field_accesses_rev: + IndexSet>, /// The return type of the function being inferred, the closure or async block if we're /// currently within one. /// @@ -598,6 +605,7 @@ impl<'a> InferenceContext<'a> { InferenceContext { result: InferenceResult::default(), table: unify::InferenceTable::new(db, trait_env), + tuple_field_accesses_rev: Default::default(), return_ty: TyKind::Error.intern(Interner), // set in collect_* calls resume_yield_tys: None, return_coercion: None, @@ -621,7 +629,13 @@ impl<'a> InferenceContext<'a> { // used this function for another workaround, mention it here. If you really need this function and believe that // there is no problem in it being `pub(crate)`, remove this comment. pub(crate) fn resolve_all(self) -> InferenceResult { - let InferenceContext { mut table, mut result, deferred_cast_checks, .. } = self; + let InferenceContext { + mut table, + mut result, + deferred_cast_checks, + tuple_field_accesses_rev, + .. + } = self; // Destructure every single field so whenever new fields are added to `InferenceResult` we // don't forget to handle them here. let InferenceResult { @@ -645,6 +659,7 @@ impl<'a> InferenceContext<'a> { // to resolve them here. closure_info: _, mutated_bindings_in_closure: _, + tuple_field_access_types: _, } = &mut result; table.fallback_if_possible(); @@ -720,6 +735,11 @@ impl<'a> InferenceContext<'a> { for adjustment in pat_adjustments.values_mut().flatten() { *adjustment = table.resolve_completely(adjustment.clone()); } + result.tuple_field_access_types = tuple_field_accesses_rev + .into_iter() + .enumerate() + .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst.clone()))) + .collect(); result } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 58b4f29ec8c5..ac00b00fcd97 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -7,12 +7,13 @@ use chalk_ir::{ fold::{FallibleTypeFolder, TypeFoldable}, AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause, }; +use either::Either; use hir_def::{ data::adt::VariantData, hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp}, lang_item::LangItem, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, - DefWithBodyId, FieldId, HasModule, VariantId, + DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, }; use hir_expand::name; use rustc_hash::FxHashMap; @@ -186,7 +187,7 @@ impl CapturedItem { result = format!("*{result}"); field_need_paren = true; } - ProjectionElem::Field(f) => { + ProjectionElem::Field(Either::Left(f)) => { if field_need_paren { result = format!("({result})"); } @@ -207,7 +208,15 @@ impl CapturedItem { result = format!("{result}.{field}"); field_need_paren = false; } - &ProjectionElem::TupleOrClosureField(field) => { + ProjectionElem::Field(Either::Right(f)) => { + let field = f.index; + if field_need_paren { + result = format!("({result})"); + } + result = format!("{result}.{field}"); + field_need_paren = false; + } + &ProjectionElem::ClosureField(field) => { if field_need_paren { result = format!("({result})"); } @@ -329,15 +338,10 @@ impl InferenceContext<'_> { } } } - Expr::Field { expr, name } => { + Expr::Field { expr, name: _ } => { let mut place = self.place_of_expr(*expr)?; - if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) { - let index = name.as_tuple_index()?; - place.projections.push(ProjectionElem::TupleOrClosureField(index)) - } else { - let field = self.result.field_resolution(tgt_expr)?; - place.projections.push(ProjectionElem::Field(field)); - } + let field = self.result.field_resolution(tgt_expr)?; + place.projections.push(ProjectionElem::Field(field)); return Some(place); } Expr::UnaryOp { expr, op: UnaryOp::Deref } => { @@ -825,7 +829,10 @@ impl InferenceContext<'_> { let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); for (arg, i) in it { let mut p = place.clone(); - p.projections.push(ProjectionElem::TupleOrClosureField(i)); + p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // dummy this, as its unused anyways + index: i as u32, + }))); self.consume_with_pat(p, *arg); } } @@ -850,10 +857,10 @@ impl InferenceContext<'_> { continue; }; let mut p = place.clone(); - p.projections.push(ProjectionElem::Field(FieldId { + p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant.into(), local_id, - })); + }))); self.consume_with_pat(p, arg); } } @@ -894,10 +901,10 @@ impl InferenceContext<'_> { al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); for (arg, (i, _)) in it { let mut p = place.clone(); - p.projections.push(ProjectionElem::Field(FieldId { + p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant.into(), local_id: i, - })); + }))); self.consume_with_pat(p, *arg); } } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index b8a7d3ebf791..123a9075683b 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -6,6 +6,7 @@ use std::{ }; use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind}; +use either::Either; use hir_def::{ generics::TypeOrConstParamData, hir::{ @@ -13,7 +14,7 @@ use hir_def::{ }, lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs}, - BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, + BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId, }; use hir_expand::name::{name, Name}; use stdx::always; @@ -1406,7 +1407,7 @@ impl InferenceContext<'_> { &mut self, receiver_ty: &Ty, name: &Name, - ) -> Option<(Ty, Option, Vec, bool)> { + ) -> Option<(Ty, Either, Vec, bool)> { let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { @@ -1418,7 +1419,20 @@ impl InferenceContext<'_> { .get(idx) .map(|a| a.assert_ty_ref(Interner)) .cloned() - .map(|ty| (None, ty)) + .map(|ty| { + ( + Either::Right(TupleFieldId { + tuple: TupleId( + self.tuple_field_accesses_rev + .insert_full(substs.clone()) + .0 + as u32, + ), + index: idx as u32, + }), + ty, + ) + }) }); } TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { @@ -1444,7 +1458,7 @@ impl InferenceContext<'_> { let ty = self.db.field_types(field_id.parent)[field_id.local_id] .clone() .substitute(Interner, ¶meters); - Some((Some(field_id), ty)) + Some((Either::Left(field_id), ty)) }); Some(match res { @@ -1464,7 +1478,7 @@ impl InferenceContext<'_> { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); - (ty, Some(field_id), adjustments, false) + (ty, Either::Left(field_id), adjustments, false) } }) } @@ -1487,11 +1501,9 @@ impl InferenceContext<'_> { match self.lookup_field(&receiver_ty, name) { Some((ty, field_id, adjustments, is_public)) => { self.write_expr_adj(receiver, adjustments); - if let Some(field_id) = field_id { - self.result.field_resolutions.insert(tgt_expr, field_id); - } + self.result.field_resolutions.insert(tgt_expr, field_id); if !is_public { - if let Some(field) = field_id { + if let Either::Left(field) = field_id { // FIXME: Merge this diagnostic into UnresolvedField? self.result .diagnostics @@ -1581,9 +1593,7 @@ impl InferenceContext<'_> { { Some((ty, field_id, adjustments, _public)) => { self.write_expr_adj(receiver, adjustments); - if let Some(field_id) = field_id { - self.result.field_resolutions.insert(tgt_expr, field_id); - } + self.result.field_resolutions.insert(tgt_expr, field_id); Some(ty) } None => None, diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index f1795e71d945..20e035c8b2e6 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -14,9 +14,10 @@ use crate::{ }; use base_db::CrateId; use chalk_ir::Mutability; +use either::Either; use hir_def::{ hir::{BindingId, Expr, ExprId, Ordering, PatId}, - DefWithBodyId, FieldId, StaticId, UnionId, VariantId, + DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId, }; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; @@ -124,9 +125,9 @@ impl Operand { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ProjectionElem { Deref, - Field(FieldId), + Field(Either), // FIXME: get rid of this, and use FieldId for tuples and closures - TupleOrClosureField(usize), + ClosureField(usize), Index(V), ConstantIndex { offset: u64, from_end: bool }, Subslice { from: u64, to: u64 }, @@ -161,7 +162,7 @@ impl ProjectionElem { return TyKind::Error.intern(Interner); } }, - ProjectionElem::Field(f) => match &base.kind(Interner) { + ProjectionElem::Field(Either::Left(f)) => match &base.kind(Interner) { TyKind::Adt(_, subst) => { db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst) } @@ -170,19 +171,25 @@ impl ProjectionElem { return TyKind::Error.intern(Interner); } }, - ProjectionElem::TupleOrClosureField(f) => match &base.kind(Interner) { + ProjectionElem::Field(Either::Right(f)) => match &base.kind(Interner) { TyKind::Tuple(_, subst) => subst .as_slice(Interner) - .get(*f) + .get(f.index as usize) .map(|x| x.assert_ty_ref(Interner)) .cloned() .unwrap_or_else(|| { never!("Out of bound tuple field"); TyKind::Error.intern(Interner) }), + _ => { + never!("Only tuple has tuple field"); + return TyKind::Error.intern(Interner); + } + }, + ProjectionElem::ClosureField(f) => match &base.kind(Interner) { TyKind::Closure(id, subst) => closure_field(*id, subst, *f), _ => { - never!("Only tuple or closure has tuple or closure field"); + never!("Only closure has closure field"); return TyKind::Error.intern(Interner); } }, diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 74c5efd6c3f4..e79c87a02f40 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -205,7 +205,7 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Field(_) - | ProjectionElem::TupleOrClosureField(_) + | ProjectionElem::ClosureField(_) | ProjectionElem::Index(_) => { is_part_of = true; } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index fbfb6ff8cddd..95cea46f9ebb 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -720,13 +720,19 @@ impl Evaluator<'_> { self.size_of_sized(&inner_ty, locals, "array inner type should be sized")?; addr = addr.offset(ty_size * (from as usize)); } - &ProjectionElem::TupleOrClosureField(f) => { + &ProjectionElem::ClosureField(f) => { let layout = self.layout(&prev_ty)?; let offset = layout.fields.offset(f).bytes_usize(); addr = addr.offset(offset); - metadata = None; // tuple field is always sized + metadata = None; } - ProjectionElem::Field(f) => { + ProjectionElem::Field(Either::Right(f)) => { + let layout = self.layout(&prev_ty)?; + let offset = layout.fields.offset(f.index as usize).bytes_usize(); + addr = addr.offset(offset); + metadata = None; // tuple field is always sized FIXME: This is wrong, the tail can be unsized + } + ProjectionElem::Field(Either::Left(f)) => { let layout = self.layout(&prev_ty)?; let variant_layout = match &layout.variants { Variants::Single { .. } => &layout, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 639fabc198c1..c27c1ff7a2a8 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -15,7 +15,7 @@ use hir_def::{ path::Path, resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs}, AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId, - Lookup, TraitId, TypeOrConstParamId, + Lookup, TraitId, TupleId, TypeOrConstParamId, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -828,12 +828,12 @@ impl<'ctx> MirLowerCtx<'ctx> { Some(it) => it, None => { let p = sp.project( - ProjectionElem::Field(FieldId { + ProjectionElem::Field(Either::Left(FieldId { parent: variant_id, local_id: LocalFieldId::from_raw(RawIdx::from( i as u32, )), - }), + })), &mut self.result.projection_store, ); Operand::Copy(p) @@ -855,7 +855,10 @@ impl<'ctx> MirLowerCtx<'ctx> { let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; let place = place.project( - PlaceElem::Field(FieldId { parent: union_id.into(), local_id }), + PlaceElem::Field(Either::Left(FieldId { + parent: union_id.into(), + local_id, + })), &mut self.result.projection_store, ); self.lower_expr_to_place(*expr, place, current) @@ -1142,8 +1145,8 @@ impl<'ctx> MirLowerCtx<'ctx> { .map(|it| match it { ProjectionElem::Deref => ProjectionElem::Deref, ProjectionElem::Field(it) => ProjectionElem::Field(it), - ProjectionElem::TupleOrClosureField(it) => { - ProjectionElem::TupleOrClosureField(it) + ProjectionElem::ClosureField(it) => { + ProjectionElem::ClosureField(it) } ProjectionElem::ConstantIndex { offset, from_end } => { ProjectionElem::ConstantIndex { offset, from_end } @@ -1273,7 +1276,10 @@ impl<'ctx> MirLowerCtx<'ctx> { Expr::Tuple { exprs, is_assignee_expr: _ } => { for (i, expr) in exprs.iter().enumerate() { let rhs = rhs.project( - ProjectionElem::TupleOrClosureField(i), + ProjectionElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // Dummy this as its unused + index: i as u32, + })), &mut self.result.projection_store, ); let Some(c) = self.lower_destructing_assignment(current, *expr, rhs, span)? @@ -1337,11 +1343,14 @@ impl<'ctx> MirLowerCtx<'ctx> { fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<()> { if let Expr::Field { expr, name } = &self.body[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { - let index = name - .as_tuple_index() - .ok_or(MirLowerError::TypeError("named field on tuple"))?; + let index = + name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))? + as u32; *place = place.project( - ProjectionElem::TupleOrClosureField(index), + ProjectionElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // dummy as its unused + index, + })), &mut self.result.projection_store, ) } else { @@ -2041,10 +2050,11 @@ pub fn mir_body_for_closure_query( match (it, y) { (ProjectionElem::Deref, ProjectionElem::Deref) => (), (ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (), - ( - ProjectionElem::TupleOrClosureField(it), - ProjectionElem::TupleOrClosureField(y), - ) if it == y => (), + (ProjectionElem::ClosureField(it), ProjectionElem::ClosureField(y)) + if it == y => + { + () + } _ => return false, } } @@ -2054,7 +2064,7 @@ pub fn mir_body_for_closure_query( Some(it) => { p.local = closure_local; let mut next_projs = closure_projection.clone(); - next_projs.push(PlaceElem::TupleOrClosureField(it.1)); + next_projs.push(PlaceElem::ClosureField(it.1)); let prev_projs = p.projection; if it.0.kind != CaptureKind::ByValue { next_projs.push(ProjectionElem::Deref); diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 1120bb1c1123..97ff65a455d3 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -108,7 +108,12 @@ impl MirLowerCtx<'_> { current_else, args, *ellipsis, - (0..subst.len(Interner)).map(|i| PlaceElem::TupleOrClosureField(i)), + (0..subst.len(Interner)).map(|i| { + PlaceElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // Dummy as it is unused + index: i as u32, + })) + }), &(&mut cond_place), mode, )? @@ -566,7 +571,10 @@ impl MirLowerCtx<'_> { let field_id = variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; Ok(( - PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }), + PlaceElem::Field(Either::Left(FieldId { + parent: v.into(), + local_id: field_id, + })), x.pat, )) }) @@ -574,10 +582,9 @@ impl MirLowerCtx<'_> { self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? } AdtPatternShape::Tuple { args, ellipsis } => { - let fields = variant_data - .fields() - .iter() - .map(|(x, _)| PlaceElem::Field(FieldId { parent: v.into(), local_id: x })); + let fields = variant_data.fields().iter().map(|(x, _)| { + PlaceElem::Field(Either::Left(FieldId { parent: v.into(), local_id: x })) + }); self.pattern_match_tuple_like( current, current_else, diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index a91f90bc249e..366c2f662b54 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -5,6 +5,7 @@ use std::{ mem, }; +use either::Either; use hir_def::{body::Body, hir::BindingId}; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -298,7 +299,7 @@ impl<'a> MirPrettyCtx<'a> { f(this, local, head); w!(this, ")"); } - ProjectionElem::Field(field) => { + ProjectionElem::Field(Either::Left(field)) => { let variant_data = field.parent.variant_data(this.db.upcast()); let name = &variant_data.fields()[field.local_id].name; match field.parent { @@ -320,7 +321,11 @@ impl<'a> MirPrettyCtx<'a> { } } } - ProjectionElem::TupleOrClosureField(it) => { + ProjectionElem::Field(Either::Right(field)) => { + f(this, local, head); + w!(this, ".{}", field.index); + } + ProjectionElem::ClosureField(it) => { f(this, local, head); w!(this, ".{}", it); } diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 5847c8a9fb52..9b99b141fc5e 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -19,8 +19,8 @@ use hir_ty::{ use crate::{ Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, SelfParam, - Static, Struct, Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, - Union, Variant, + Static, Struct, Trait, TraitAlias, TupleField, TyBuilder, Type, TypeAlias, TypeOrConstParam, + TypeParam, Union, Variant, }; impl HirDisplay for Function { @@ -257,6 +257,13 @@ impl HirDisplay for Field { } } +impl HirDisplay for TupleField { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + write!(f, "pub {}: ", self.name().display(f.db.upcast()))?; + self.ty(f.db).hir_fmt(f) + } +} + impl HirDisplay for Variant { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index fc6c2aeb3bea..4e4b758264f4 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -55,7 +55,7 @@ use hir_def::{ AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, - Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, + Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, }; use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind}; @@ -1038,6 +1038,29 @@ pub struct Field { pub(crate) id: LocalFieldId, } +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +pub struct TupleField { + pub owner: DefWithBodyId, + pub tuple: TupleId, + pub index: u32, +} + +impl TupleField { + pub fn name(&self) -> Name { + Name::new_tuple_field(self.index as usize) + } + + pub fn ty(&self, db: &dyn HirDatabase) -> Type { + let ty = db.infer(self.owner).tuple_field_access_types[&self.tuple] + .as_slice(Interner) + .get(self.index as usize) + .and_then(|arg| arg.ty(Interner)) + .cloned() + .unwrap_or_else(|| TyKind::Error.intern(Interner)); + Type { env: db.trait_environment_for_body(self.owner), ty } + } +} + #[derive(Debug, PartialEq, Eq)] pub enum FieldSource { Named(ast::RecordField), diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index fdc604a006fc..a82ae308fae6 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -40,7 +40,7 @@ use crate::{ Access, Adjust, Adjustment, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Struct, ToolModule, Trait, - Type, TypeAlias, TypeParam, VariantDef, + TupleField, Type, TypeAlias, TypeParam, VariantDef, }; pub enum DescendPreference { @@ -1085,14 +1085,14 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } - pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option { + pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option> { self.analyze(field.syntax())?.resolve_field(self.db, field) } pub fn resolve_field_fallback( &self, field: &ast::FieldExpr, - ) -> Option> { + ) -> Option, Function>> { self.analyze(field.syntax())?.resolve_field_fallback(self.db, field) } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 54b4d81012f3..73f8db762ae8 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -50,7 +50,7 @@ use triomphe::Arc; use crate::{ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static, - Struct, ToolModule, Trait, TraitAlias, Type, TypeAlias, Variant, + Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant, }; /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of @@ -297,7 +297,11 @@ impl SourceAnalyzer { Some((f_in_trait, substs)) => Some(Either::Left( self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into(), )), - None => inference_result.field_resolution(expr_id).map(Into::into).map(Either::Right), + None => inference_result + .field_resolution(expr_id) + .and_then(Either::left) + .map(Into::into) + .map(Either::Right), } } @@ -305,20 +309,28 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, field: &ast::FieldExpr, - ) -> Option { + ) -> Option> { + let &(def, ..) = self.def.as_ref()?; let expr_id = self.expr_id(db, &field.clone().into())?; - self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) + self.infer.as_ref()?.field_resolution(expr_id).map(|it| { + it.map_either(Into::into, |f| TupleField { owner: def, tuple: f.tuple, index: f.index }) + }) } pub(crate) fn resolve_field_fallback( &self, db: &dyn HirDatabase, field: &ast::FieldExpr, - ) -> Option> { + ) -> Option, Function>> { + let &(def, ..) = self.def.as_ref()?; let expr_id = self.expr_id(db, &field.clone().into())?; let inference_result = self.infer.as_ref()?; match inference_result.field_resolution(expr_id) { - Some(field) => Some(Either::Left(field.into())), + Some(field) => Some(Either::Left(field.map_either(Into::into, |f| TupleField { + owner: def, + tuple: f.tuple, + index: f.index, + }))), None => inference_result.method_resolution(expr_id).map(|(f, substs)| { Either::Right(self.resolve_impl_method_or_trait_def(db, f, substs).into()) }), diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 410b8304592d..8f55f30a2dd4 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -6,11 +6,13 @@ // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). use arrayvec::ArrayVec; +use either::Either; use hir::{ Adt, AsAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternCrateDecl, Field, Function, GenericParam, HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module, ModuleDef, Name, PathResolution, - Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant, VariantDef, Visibility, + Semantics, Static, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, + Visibility, }; use stdx::{format_to, impl_from}; use syntax::{ @@ -27,6 +29,7 @@ use crate::RootDatabase; pub enum Definition { Macro(Macro), Field(Field), + TupleField(TupleField), Module(Module), Function(Function), Adt(Adt), @@ -78,9 +81,10 @@ impl Definition { Definition::Label(it) => it.module(db), Definition::ExternCrateDecl(it) => it.module(db), Definition::DeriveHelper(it) => it.derive().module(db), - Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => { - return None - } + Definition::BuiltinAttr(_) + | Definition::BuiltinType(_) + | Definition::TupleField(_) + | Definition::ToolModule(_) => return None, }; Some(module) } @@ -105,7 +109,7 @@ impl Definition { Definition::TypeAlias(it) => it.visibility(db), Definition::Variant(it) => it.visibility(db), Definition::ExternCrateDecl(it) => it.visibility(db), - Definition::BuiltinType(_) => Visibility::Public, + Definition::BuiltinType(_) | Definition::TupleField(_) => Visibility::Public, Definition::Macro(_) => return None, Definition::BuiltinAttr(_) | Definition::ToolModule(_) @@ -132,6 +136,7 @@ impl Definition { Definition::TraitAlias(it) => it.name(db), Definition::TypeAlias(it) => it.name(db), Definition::BuiltinType(it) => it.name(), + Definition::TupleField(it) => it.name(), Definition::SelfType(_) => return None, Definition::Local(it) => it.name(db), Definition::GenericParam(it) => it.name(db), @@ -194,6 +199,7 @@ impl Definition { } Definition::ToolModule(_) => None, Definition::DeriveHelper(_) => None, + Definition::TupleField(_) => None, }; docs.or_else(|| { @@ -211,6 +217,7 @@ impl Definition { let label = match *self { Definition::Macro(it) => it.display(db).to_string(), Definition::Field(it) => it.display(db).to_string(), + Definition::TupleField(it) => it.display(db).to_string(), Definition::Module(it) => it.display(db).to_string(), Definition::Function(it) => it.display(db).to_string(), Definition::Adt(it) => it.display(db).to_string(), @@ -630,9 +637,11 @@ impl NameRefClass { ast::FieldExpr(field_expr) => { sema.resolve_field_fallback(&field_expr) .map(|it| { - it.map_left(Definition::Field) - .map_right(Definition::Function) - .either(NameRefClass::Definition, NameRefClass::Definition) + NameRefClass::Definition(match it { + Either::Left(Either::Left(field)) => Definition::Field(field), + Either::Left(Either::Right(field)) => Definition::TupleField(field), + Either::Right(fun) => Definition::Function(fun), + }) }) }, ast::RecordPatField(record_pat_field) => { diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 7f28965885ad..f694f7160dea 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -198,6 +198,7 @@ impl Definition { Definition::SelfType(_) => return None, Definition::BuiltinAttr(_) => return None, Definition::ToolModule(_) => return None, + Definition::TupleField(_) => return None, // FIXME: This should be doable in theory Definition::DeriveHelper(_) => return None, }; diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index a36082bafcf2..4b0ecb9cf908 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -219,6 +219,7 @@ pub(crate) fn resolve_doc_path_for_def( Definition::BuiltinAttr(_) | Definition::ToolModule(_) | Definition::BuiltinType(_) + | Definition::TupleField(_) | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) @@ -639,6 +640,7 @@ fn filename_and_frag_for_def( } Definition::Local(_) | Definition::GenericParam(_) + | Definition::TupleField(_) | Definition::Label(_) | Definition::BuiltinAttr(_) | Definition::ToolModule(_) diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 94ddd162de4e..486329dadeda 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -179,7 +179,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati MacroKind::Attr => Attribute, MacroKind::ProcMacro => Macro, }, - Definition::Field(..) => Field, + Definition::Field(..) | Definition::TupleField(..) => Field, Definition::Module(..) => Module, Definition::Function(it) => { if it.as_assoc_item(db).is_some() { @@ -361,6 +361,9 @@ pub(crate) fn def_to_moniker( Definition::Field(it) => { MonikerDescriptor { name: it.name(db).display(db).to_string(), desc } } + Definition::TupleField(it) => { + MonikerDescriptor { name: it.name().display(db).to_string(), desc } + } Definition::Adt(adt) => { MonikerDescriptor { name: adt.name(db).display(db).to_string(), desc } } diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index e62f5a43d0ee..bc0574ca86ed 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -237,7 +237,7 @@ impl TryToNav for Definition { Definition::TraitAlias(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), Definition::ExternCrateDecl(it) => Some(it.try_to_nav(db)?), - Definition::BuiltinType(_) => None, + Definition::BuiltinType(_) | Definition::TupleField(_) => None, Definition::ToolModule(_) => None, Definition::BuiltinAttr(_) => None, // FIXME: The focus range should be set to the helper declaration diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 0558f658fd19..d686652bb3ec 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -1,5 +1,6 @@ //! Computes color for a single element. +use either::Either; use hir::{AsAssocItem, HasVisibility, MacroFileIdExt, Semantics}; use ide_db::{ defs::{Definition, IdentClass, NameClass, NameRefClass}, @@ -359,7 +360,9 @@ pub(super) fn highlight_def( let db = sema.db; let mut h = match def { Definition::Macro(m) => Highlight::new(HlTag::Symbol(m.kind(sema.db).into())), - Definition::Field(_) => Highlight::new(HlTag::Symbol(SymbolKind::Field)), + Definition::Field(_) | Definition::TupleField(_) => { + Highlight::new(HlTag::Symbol(SymbolKind::Field)) + } Definition::Module(module) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module)); if module.is_crate_root() { @@ -647,8 +650,11 @@ fn highlight_name_ref_by_syntax( let h = HlTag::Symbol(SymbolKind::Field); let is_union = ast::FieldExpr::cast(parent) .and_then(|field_expr| sema.resolve_field(&field_expr)) - .map_or(false, |field| { - matches!(field.parent_def(sema.db), hir::VariantDef::Union(_)) + .map_or(false, |field| match field { + Either::Left(field) => { + matches!(field.parent_def(sema.db), hir::VariantDef::Union(_)) + } + Either::Right(_) => false, }); if is_union { h | HlMod::Unsafe diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 71f4d07245d2..6bf13ffd06fc 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -301,7 +301,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::TypeAlias(_) => SymbolKind::TypeAlias, Definition::BuiltinType(_) => return HlTag::BuiltinType, Definition::Macro(_) => SymbolKind::Macro, - Definition::Field(_) => SymbolKind::Field, + Definition::Field(_) | Definition::TupleField(_) => SymbolKind::Field, Definition::SelfType(_) => SymbolKind::Impl, Definition::Local(_) => SymbolKind::Local, Definition::GenericParam(gp) => match gp { diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index e8b3a38c9e0f..4063cf9f7570 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -96,7 +96,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd include!(concat!("foo/", "foo.rs")); fn main() { - format_args!("Hello, {}!", 92); + format_args!("Hello, {}!", (92,).0); dont_color_me_braces!(); noop!(noop!(1)); } diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index afb6c555b4af..864c6d1cad79 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -103,7 +103,7 @@ macro without_args { include!(concat!("foo/", "foo.rs")); fn main() { - format_args!("Hello, {}!", 92); + format_args!("Hello, {}!", (92,).0); dont_color_me_braces!(); noop!(noop!(1)); } From 94a59d6f62c36e0f80a4fa36aebe2878a20079e7 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 6 Jan 2024 09:33:51 -0500 Subject: [PATCH 129/257] Strip lld-wrapper binaries This cuts down on the amount of data we need to ship and users need to keep on disk for each Rust toolchain. As noted in the added comment, there's not much going on in these executables, so the added benefit of symbols and debuginfo isn't large, while the cost is not insignificant. This takes each of the binaries (we store 4 identical copies under different names) from 3.7MB to 384KB. --- Cargo.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 9b11ae8744b4..039150788386 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,6 +104,14 @@ gimli.debug = 0 miniz_oxide.debug = 0 object.debug = 0 +# These are very thin wrappers around executing lld with the right binary name. +# Basically nothing within them can go wrong without having been explicitly logged anyway. +# We ship these in every rustc tarball and even after compression they add up +# to around 0.6MB of data every user needs to download (and 15MB on disk). +[profile.release.package.lld-wrapper] +debug = 0 +strip = true + [patch.crates-io] # See comments in `library/rustc-std-workspace-core/README.md` for what's going on # here From c51828ae8b31bda26b0d19637aad75f871d661ba Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 6 Jan 2024 18:46:25 +0300 Subject: [PATCH 130/257] tests: Normalize `\r\n` to `\n` in some run-make tests The output is produced by printf from C code in these cases, and printf prints in text mode, which means `\n` will be printed as `\r\n` on Windows. In --bless mode the new output with `\r\n` will replace expected output in `tests/run-make/raw-dylib-*\output.txt` files, which use \n, always resulting in dirty files in the repo. --- tests/run-make/raw-dylib-c/Makefile | 2 +- tests/run-make/raw-dylib-inline-cross-dylib/Makefile | 2 +- tests/run-make/raw-dylib-link-ordinal/Makefile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/run-make/raw-dylib-c/Makefile b/tests/run-make/raw-dylib-c/Makefile index 06e7935c0264..af5c4a6edd7b 100644 --- a/tests/run-make/raw-dylib-c/Makefile +++ b/tests/run-make/raw-dylib-c/Makefile @@ -17,7 +17,7 @@ else $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll endif - "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt + "$(TMPDIR)"/driver | tr -d '\r' > "$(TMPDIR)"/output.txt "$(TMPDIR)"/raw_dylib_test_bin > "$(TMPDIR)"/output_bin.txt ifdef RUSTC_BLESS_TEST diff --git a/tests/run-make/raw-dylib-inline-cross-dylib/Makefile b/tests/run-make/raw-dylib-inline-cross-dylib/Makefile index 195b5fda56fd..6b44b40e253e 100644 --- a/tests/run-make/raw-dylib-inline-cross-dylib/Makefile +++ b/tests/run-make/raw-dylib-inline-cross-dylib/Makefile @@ -26,5 +26,5 @@ else $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll endif - $(call RUN,driver) > "$(TMPDIR)"/output.txt + $(call RUN,driver) | tr -d '\r' > "$(TMPDIR)"/output.txt $(RUSTC_TEST_OP) "$(TMPDIR)"/output.txt output.txt diff --git a/tests/run-make/raw-dylib-link-ordinal/Makefile b/tests/run-make/raw-dylib-link-ordinal/Makefile index 49e959cdb5b8..3cf1300c243a 100644 --- a/tests/run-make/raw-dylib-link-ordinal/Makefile +++ b/tests/run-make/raw-dylib-link-ordinal/Makefile @@ -13,5 +13,5 @@ ifdef IS_MSVC else $(CC) "$(TMPDIR)"/exporter.obj exporter.def -shared -o "$(TMPDIR)"/exporter.dll endif - "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt + "$(TMPDIR)"/driver | tr -d '\r' > "$(TMPDIR)"/output.txt $(RUSTC_TEST_OP) "$(TMPDIR)"/output.txt output.txt From c9c4053eed8ec7cfc66bf51743cdc144abd593f6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 6 Jan 2024 16:58:15 +0100 Subject: [PATCH 131/257] More aliases --- crates/hir-ty/src/interner.rs | 232 +++++++++++++++++----------------- crates/hir-ty/src/lib.rs | 13 +- 2 files changed, 130 insertions(+), 115 deletions(-) diff --git a/crates/hir-ty/src/interner.rs b/crates/hir-ty/src/interner.rs index e4dd4b86cf99..7d3539edad1c 100644 --- a/crates/hir-ty/src/interner.rs +++ b/crates/hir-ty/src/interner.rs @@ -1,9 +1,15 @@ //! Implementation of the Chalk `Interner` trait, which allows customizing the //! representation of the various objects Chalk deals with (types, goals etc.). -use crate::{chalk_db, tls, ConstScalar, GenericArg}; +use crate::{ + chalk_db, tls, AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, + ConstScalar, Constraint, Constraints, FnDefId, GenericArg, GenericArgData, Goal, GoalData, + Goals, InEnvironment, Lifetime, LifetimeData, OpaqueTy, OpaqueTyId, ProgramClause, + ProgramClauseData, ProgramClauses, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, + Substitution, Ty, TyData, TyKind, VariableKind, VariableKinds, +}; use base_db::salsa::InternId; -use chalk_ir::{Goal, GoalData}; +use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variance}; use hir_def::TypeAliasId; use intern::{impl_internable, Interned}; use smallvec::SmallVec; @@ -30,37 +36,70 @@ impl std::ops::Deref for InternedWrapper { } } +#[derive(Eq)] +pub struct PreHashedWrapper(T, u64); + +impl fmt::Debug for PreHashedWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl PartialEq for PreHashedWrapper { + fn eq(&self, other: &Self) -> bool { + self.1 == other.1 && self.0 == other.0 + } +} + +impl std::ops::Deref for PreHashedWrapper { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::hash::Hash for PreHashedWrapper { + fn hash(&self, state: &mut H) { + state.write_u64(self.1); + } +} + impl_internable!( - InternedWrapper>>, + InternedWrapper>, InternedWrapper>, - InternedWrapper>, - InternedWrapper>, - InternedWrapper>, + InternedWrapper, + InternedWrapper, + InternedWrapper, InternedWrapper, - InternedWrapper>>, - InternedWrapper>>, - InternedWrapper>>, - InternedWrapper>, + InternedWrapper>, + InternedWrapper>, + InternedWrapper>, + InternedWrapper>, + // InternedWrapper>, ); impl chalk_ir::interner::Interner for Interner { - type InternedType = Interned>>; - type InternedLifetime = Interned>>; - type InternedConst = Interned>>; + type InternedType = Interned>; + type InternedLifetime = Interned>; + type InternedConst = Interned>; type InternedConcreteConst = ConstScalar; - type InternedGenericArg = chalk_ir::GenericArgData; - type InternedGoal = Arc>; - type InternedGoals = Vec>; + type InternedGenericArg = GenericArgData; + // We could do the following, but that saves "only" 20mb on self while increasing inferecene + // time by ~2.5% + // type InternedGoal = Interned>; + // type InternedGoal = Interned>>; + type InternedGoal = Arc; + type InternedGoals = Vec; type InternedSubstitution = Interned>>; - type InternedProgramClauses = Interned>>>; - type InternedProgramClause = chalk_ir::ProgramClauseData; - type InternedQuantifiedWhereClauses = - Interned>>>; - type InternedVariableKinds = Interned>>>; - type InternedCanonicalVarKinds = - Interned>>>; - type InternedConstraints = Vec>>; - type InternedVariances = Interned>>; + type InternedProgramClauses = Interned>>; + type InternedProgramClause = ProgramClauseData; + type InternedQuantifiedWhereClauses = Interned>>; + type InternedVariableKinds = Interned>>; + type InternedCanonicalVarKinds = Interned>>; + // type InternedConstraints = SmallVec<[InEnvironment; 1]>; + type InternedConstraints = Vec>; + type InternedVariances = SmallVec<[Variance; 16]>; type DefId = InternId; type InternedAdtId = hir_def::AdtId; type Identifier = TypeAliasId; @@ -88,68 +127,51 @@ impl chalk_ir::interner::Interner for Interner { } fn debug_opaque_ty_id( - opaque_ty_id: chalk_ir::OpaqueTyId, + opaque_ty_id: OpaqueTyId, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "OpaqueTy#{}", opaque_ty_id.0)) } - fn debug_fn_def_id( - fn_def_id: chalk_ir::FnDefId, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { + fn debug_fn_def_id(fn_def_id: FnDefId, fmt: &mut fmt::Formatter<'_>) -> Option { tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt))) } fn debug_closure_id( - _fn_def_id: chalk_ir::ClosureId, + _fn_def_id: ClosureId, _fmt: &mut fmt::Formatter<'_>, ) -> Option { None } - fn debug_alias( - alias: &chalk_ir::AliasTy, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { + fn debug_alias(alias: &AliasTy, fmt: &mut fmt::Formatter<'_>) -> Option { use std::fmt::Debug; match alias { - chalk_ir::AliasTy::Projection(projection_ty) => { - Interner::debug_projection_ty(projection_ty, fmt) - } - chalk_ir::AliasTy::Opaque(opaque_ty) => Some(opaque_ty.fmt(fmt)), + AliasTy::Projection(projection_ty) => Interner::debug_projection_ty(projection_ty, fmt), + AliasTy::Opaque(opaque_ty) => Some(opaque_ty.fmt(fmt)), } } fn debug_projection_ty( - proj: &chalk_ir::ProjectionTy, + proj: &ProjectionTy, fmt: &mut fmt::Formatter<'_>, ) -> Option { tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt))) } - fn debug_opaque_ty( - opaque_ty: &chalk_ir::OpaqueTy, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { + fn debug_opaque_ty(opaque_ty: &OpaqueTy, fmt: &mut fmt::Formatter<'_>) -> Option { Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id)) } - fn debug_ty(ty: &chalk_ir::Ty, fmt: &mut fmt::Formatter<'_>) -> Option { + fn debug_ty(ty: &Ty, fmt: &mut fmt::Formatter<'_>) -> Option { Some(write!(fmt, "{:?}", ty.data(Interner))) } - fn debug_lifetime( - lifetime: &chalk_ir::Lifetime, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { + fn debug_lifetime(lifetime: &Lifetime, fmt: &mut fmt::Formatter<'_>) -> Option { Some(write!(fmt, "{:?}", lifetime.data(Interner))) } - fn debug_const( - constant: &chalk_ir::Const, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { + fn debug_const(constant: &Const, fmt: &mut fmt::Formatter<'_>) -> Option { Some(write!(fmt, "{:?}", constant.data(Interner))) } @@ -161,102 +183,99 @@ impl chalk_ir::interner::Interner for Interner { } fn debug_variable_kinds( - variable_kinds: &chalk_ir::VariableKinds, + variable_kinds: &VariableKinds, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", variable_kinds.as_slice(Interner))) } fn debug_variable_kinds_with_angles( - variable_kinds: &chalk_ir::VariableKinds, + variable_kinds: &VariableKinds, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", variable_kinds.inner_debug(Interner))) } fn debug_canonical_var_kinds( - canonical_var_kinds: &chalk_ir::CanonicalVarKinds, + canonical_var_kinds: &CanonicalVarKinds, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", canonical_var_kinds.as_slice(Interner))) } - fn debug_goal(goal: &Goal, fmt: &mut fmt::Formatter<'_>) -> Option { + fn debug_goal(goal: &Goal, fmt: &mut fmt::Formatter<'_>) -> Option { let goal_data = goal.data(Interner); Some(write!(fmt, "{goal_data:?}")) } - fn debug_goals( - goals: &chalk_ir::Goals, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { + fn debug_goals(goals: &Goals, fmt: &mut fmt::Formatter<'_>) -> Option { Some(write!(fmt, "{:?}", goals.debug(Interner))) } fn debug_program_clause_implication( - pci: &chalk_ir::ProgramClauseImplication, + pci: &ProgramClauseImplication, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", pci.debug(Interner))) } fn debug_program_clause( - clause: &chalk_ir::ProgramClause, + clause: &ProgramClause, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", clause.data(Interner))) } fn debug_program_clauses( - clauses: &chalk_ir::ProgramClauses, + clauses: &ProgramClauses, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", clauses.as_slice(Interner))) } fn debug_substitution( - substitution: &chalk_ir::Substitution, + substitution: &Substitution, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", substitution.debug(Interner))) } fn debug_separator_trait_ref( - separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Interner>, + separator_trait_ref: &SeparatorTraitRef<'_, Interner>, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", separator_trait_ref.debug(Interner))) } fn debug_quantified_where_clauses( - clauses: &chalk_ir::QuantifiedWhereClauses, + clauses: &QuantifiedWhereClauses, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", clauses.as_slice(Interner))) } fn debug_constraints( - _clauses: &chalk_ir::Constraints, + _clauses: &Constraints, _fmt: &mut fmt::Formatter<'_>, ) -> Option { None } - fn intern_ty(self, kind: chalk_ir::TyKind) -> Self::InternedType { + fn intern_ty(self, kind: TyKind) -> Self::InternedType { let flags = kind.compute_flags(self); - Interned::new(InternedWrapper(chalk_ir::TyData { kind, flags })) + Interned::new(InternedWrapper(TyData { kind, flags })) } - fn ty_data(self, ty: &Self::InternedType) -> &chalk_ir::TyData { + fn ty_data(self, ty: &Self::InternedType) -> &TyData { &ty.0 } - fn intern_lifetime(self, lifetime: chalk_ir::LifetimeData) -> Self::InternedLifetime { + fn intern_lifetime(self, lifetime: LifetimeData) -> Self::InternedLifetime { Interned::new(InternedWrapper(lifetime)) } - fn lifetime_data(self, lifetime: &Self::InternedLifetime) -> &chalk_ir::LifetimeData { + fn lifetime_data(self, lifetime: &Self::InternedLifetime) -> &LifetimeData { &lifetime.0 } - fn intern_const(self, constant: chalk_ir::ConstData) -> Self::InternedConst { + fn intern_const(self, constant: ConstData) -> Self::InternedConst { Interned::new(InternedWrapper(constant)) } - fn const_data(self, constant: &Self::InternedConst) -> &chalk_ir::ConstData { + fn const_data(self, constant: &Self::InternedConst) -> &ConstData { &constant.0 } @@ -269,36 +288,33 @@ impl chalk_ir::interner::Interner for Interner { !matches!(c1, ConstScalar::Bytes(..)) || !matches!(c2, ConstScalar::Bytes(..)) || (c1 == c2) } - fn intern_generic_arg( - self, - parameter: chalk_ir::GenericArgData, - ) -> Self::InternedGenericArg { + fn intern_generic_arg(self, parameter: GenericArgData) -> Self::InternedGenericArg { parameter } - fn generic_arg_data( - self, - parameter: &Self::InternedGenericArg, - ) -> &chalk_ir::GenericArgData { + fn generic_arg_data(self, parameter: &Self::InternedGenericArg) -> &GenericArgData { parameter } - fn intern_goal(self, goal: GoalData) -> Self::InternedGoal { + fn intern_goal(self, goal: GoalData) -> Self::InternedGoal { Arc::new(goal) } - fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData { + fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData { goal } fn intern_goals( self, - data: impl IntoIterator, E>>, + data: impl IntoIterator>, ) -> Result { + // let hash = + // std::hash::BuildHasher::hash_one(&BuildHasherDefault::::default(), &goal); + // Interned::new(InternedWrapper(PreHashedWrapper(goal, hash))) data.into_iter().collect() } - fn goals_data(self, goals: &Self::InternedGoals) -> &[Goal] { + fn goals_data(self, goals: &Self::InternedGoals) -> &[Goal] { goals } @@ -313,37 +329,28 @@ impl chalk_ir::interner::Interner for Interner { &substitution.as_ref().0 } - fn intern_program_clause( - self, - data: chalk_ir::ProgramClauseData, - ) -> Self::InternedProgramClause { + fn intern_program_clause(self, data: ProgramClauseData) -> Self::InternedProgramClause { data } - fn program_clause_data( - self, - clause: &Self::InternedProgramClause, - ) -> &chalk_ir::ProgramClauseData { + fn program_clause_data(self, clause: &Self::InternedProgramClause) -> &ProgramClauseData { clause } fn intern_program_clauses( self, - data: impl IntoIterator, E>>, + data: impl IntoIterator>, ) -> Result { Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) } - fn program_clauses_data( - self, - clauses: &Self::InternedProgramClauses, - ) -> &[chalk_ir::ProgramClause] { + fn program_clauses_data(self, clauses: &Self::InternedProgramClauses) -> &[ProgramClause] { clauses } fn intern_quantified_where_clauses( self, - data: impl IntoIterator, E>>, + data: impl IntoIterator>, ) -> Result { Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) } @@ -351,27 +358,24 @@ impl chalk_ir::interner::Interner for Interner { fn quantified_where_clauses_data( self, clauses: &Self::InternedQuantifiedWhereClauses, - ) -> &[chalk_ir::QuantifiedWhereClause] { + ) -> &[QuantifiedWhereClause] { clauses } fn intern_generic_arg_kinds( self, - data: impl IntoIterator, E>>, + data: impl IntoIterator>, ) -> Result { Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) } - fn variable_kinds_data( - self, - parameter_kinds: &Self::InternedVariableKinds, - ) -> &[chalk_ir::VariableKind] { + fn variable_kinds_data(self, parameter_kinds: &Self::InternedVariableKinds) -> &[VariableKind] { ¶meter_kinds.as_ref().0 } fn intern_canonical_var_kinds( self, - data: impl IntoIterator, E>>, + data: impl IntoIterator>, ) -> Result { Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) } @@ -379,30 +383,30 @@ impl chalk_ir::interner::Interner for Interner { fn canonical_var_kinds_data( self, canonical_var_kinds: &Self::InternedCanonicalVarKinds, - ) -> &[chalk_ir::CanonicalVarKind] { + ) -> &[CanonicalVarKind] { canonical_var_kinds } fn intern_constraints( self, - data: impl IntoIterator>, E>>, + data: impl IntoIterator, E>>, ) -> Result { data.into_iter().collect() } fn constraints_data( self, constraints: &Self::InternedConstraints, - ) -> &[chalk_ir::InEnvironment>] { + ) -> &[InEnvironment] { constraints } fn intern_variances( self, - data: impl IntoIterator>, + data: impl IntoIterator>, ) -> Result { - Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) + data.into_iter().collect::>() } - fn variances_data(self, variances: &Self::InternedVariances) -> &[chalk_ir::Variance] { + fn variances_data(self, variances: &Self::InternedVariances) -> &[Variance] { variances } } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index cf174feed24b..b60fbac5af8a 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -45,7 +45,7 @@ use chalk_ir::{ fold::{Shift, TypeFoldable}, interner::HasInterner, visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, - NoSolution, TyData, + NoSolution, }; use either::Either; use hir_def::{hir::ExprId, type_ref::Rawness, GeneralConstId, TypeOrConstParamId}; @@ -152,10 +152,21 @@ pub type DomainGoal = chalk_ir::DomainGoal; pub type Goal = chalk_ir::Goal; pub type AliasEq = chalk_ir::AliasEq; pub type Solution = chalk_solve::Solution; +pub type Constraint = chalk_ir::Constraint; +pub type Constraints = chalk_ir::Constraints; pub type ConstrainedSubst = chalk_ir::ConstrainedSubst; pub type Guidance = chalk_solve::Guidance; pub type WhereClause = chalk_ir::WhereClause; +pub type CanonicalVarKind = chalk_ir::CanonicalVarKind; +pub type GoalData = chalk_ir::GoalData; +pub type Goals = chalk_ir::Goals; +pub type ProgramClauseData = chalk_ir::ProgramClauseData; +pub type ProgramClause = chalk_ir::ProgramClause; +pub type ProgramClauses = chalk_ir::ProgramClauses; +pub type TyData = chalk_ir::TyData; +pub type Variances = chalk_ir::Variances; + /// A constant can have reference to other things. Memory map job is holding /// the necessary bits of memory of the const eval session to keep the constant /// meaningful. From 5125063a210ca85a86787c24fb14bebdce766488 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 6 Jan 2024 17:48:07 +0100 Subject: [PATCH 132/257] Remove unnecessary cloning --- crates/hir-ty/src/consteval.rs | 8 +-- crates/hir-ty/src/lib.rs | 2 +- crates/hir-ty/src/mir.rs | 8 +-- crates/hir-ty/src/mir/eval.rs | 24 ++++---- crates/hir-ty/src/mir/eval/shim.rs | 3 +- crates/hir-ty/src/mir/lower.rs | 57 ++++++++++--------- .../hir-ty/src/mir/lower/pattern_matching.rs | 2 +- 7 files changed, 54 insertions(+), 50 deletions(-) diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 9792d945eb8f..5528ad3ab4ab 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -142,15 +142,15 @@ pub fn intern_const_ref( LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); - ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) + ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()) } LiteralConstRef::UInt(i) => { let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); - ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) + ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()) } - LiteralConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()), + LiteralConstRef::Bool(b) => ConstScalar::Bytes(Box::new([*b as u8]), MemoryMap::default()), LiteralConstRef::Char(c) => { - ConstScalar::Bytes((*c as u32).to_le_bytes().to_vec(), MemoryMap::default()) + ConstScalar::Bytes((*c as u32).to_le_bytes().into(), MemoryMap::default()) } LiteralConstRef::Unknown => ConstScalar::Unknown, }; diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index b60fbac5af8a..964ca7ce940e 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -219,7 +219,7 @@ impl MemoryMap { /// A concrete constant value #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConstScalar { - Bytes(Vec, MemoryMap), + Bytes(Box<[u8]>, MemoryMap), // FIXME: this is a hack to get around chalk not being able to represent unevaluatable // constants UnevaluatedConst(GeneralConstId, Substitution), diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 20e035c8b2e6..7bef6f0d0f71 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -98,16 +98,16 @@ pub enum Operand { } impl Operand { - fn from_concrete_const(data: Vec, memory_map: MemoryMap, ty: Ty) -> Self { + fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap, ty: Ty) -> Self { Operand::Constant(intern_const_scalar(ConstScalar::Bytes(data, memory_map), ty)) } - fn from_bytes(data: Vec, ty: Ty) -> Self { + fn from_bytes(data: Box<[u8]>, ty: Ty) -> Self { Operand::from_concrete_const(data, MemoryMap::default(), ty) } fn const_zst(ty: Ty) -> Operand { - Self::from_bytes(vec![], ty) + Self::from_bytes(Box::default(), ty) } fn from_fn( @@ -118,7 +118,7 @@ impl Operand { let ty = chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args) .intern(Interner); - Operand::from_bytes(vec![], ty) + Operand::from_bytes(Box::default(), ty) } } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 95cea46f9ebb..4c06566e7ce0 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -527,14 +527,15 @@ pub fn interpret_mir( if evaluator.ptr_size() != std::mem::size_of::() { not_supported!("targets with different pointer size from host"); } - let bytes = evaluator.interpret_mir(body.clone(), None.into_iter())?; + let interval = evaluator.interpret_mir(body.clone(), None.into_iter())?; + let bytes = interval.get(&evaluator)?; let mut memory_map = evaluator.create_memory_map( - &bytes, + bytes, &ty, &Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() }, )?; memory_map.vtable = evaluator.vtable_map.clone(); - return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); + return Ok(intern_const_scalar(ConstScalar::Bytes(bytes.into(), memory_map), ty)); })(); ( it, @@ -803,11 +804,11 @@ impl Evaluator<'_> { }) } - fn interpret_mir( - &mut self, + fn interpret_mir<'slf>( + &'slf mut self, body: Arc, args: impl Iterator, - ) -> Result> { + ) -> Result { if let Some(it) = self.stack_depth_limit.checked_sub(1) { self.stack_depth_limit = it; } else { @@ -957,7 +958,7 @@ impl Evaluator<'_> { None => { self.code_stack = prev_code_stack; self.stack_depth_limit += 1; - return Ok(return_interval.get(self)?.to_vec()); + return Ok(return_interval); } Some(bb) => { // We don't support const promotion, so we can't truncate the stack yet. @@ -2173,7 +2174,7 @@ impl Evaluator<'_> { let arg_bytes = iter::once(Ok(closure_data)) .chain(args.iter().map(|it| Ok(it.get(&self)?.to_owned()))) .collect::>>()?; - let bytes = self + let interval = self .interpret_mir(mir_body, arg_bytes.into_iter().map(IntervalOrOwned::Owned)) .map_err(|e| { MirEvalError::InFunction( @@ -2181,7 +2182,7 @@ impl Evaluator<'_> { vec![(Either::Right(closure), span, locals.body.owner)], ) })?; - destination.write_from_bytes(self, &bytes)?; + destination.write_from_interval(self, interval)?; Ok(None) } @@ -2374,7 +2375,7 @@ impl Evaluator<'_> { vec![(Either::Left(def), span, locals.body.owner)], ) })?; - destination.write_from_bytes(self, &result)?; + destination.write_from_interval(self, result)?; None }) } @@ -2680,11 +2681,12 @@ pub fn render_const_using_debug_impl( ) else { not_supported!("std::fmt::format not found"); }; - let message_string = evaluator.interpret_mir( + let interval = evaluator.interpret_mir( db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, [IntervalOrOwned::Borrowed(Interval { addr: a3, size: evaluator.ptr_size() * 6 })] .into_iter(), )?; + let message_string = interval.get(&evaluator)?; let addr = Address::from_bytes(&message_string[evaluator.ptr_size()..2 * evaluator.ptr_size()])?; let size = from_bytes!(usize, message_string[2 * evaluator.ptr_size()..]); diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 2de99e41659c..ff26a3d0be17 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -322,12 +322,13 @@ impl Evaluator<'_> { let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") }; - let message_string = self.interpret_mir( + let interval = self.interpret_mir( self.db .mir_body(format_fn.into()) .map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.map(|x| IntervalOrOwned::Owned(x.clone())), )?; + let message_string = interval.get(self)?; let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]); diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index c27c1ff7a2a8..a94da818b622 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -540,7 +540,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.write_bytes_to_place( then_target, place.clone(), - vec![1], + Box::new([1]), TyBuilder::bool(), MirSpan::Unknown, )?; @@ -548,7 +548,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.write_bytes_to_place( else_target, place, - vec![0], + Box::new([0]), TyBuilder::bool(), MirSpan::Unknown, )?; @@ -602,7 +602,7 @@ impl<'ctx> MirLowerCtx<'ctx> { generic_args, ) .intern(Interner); - let func = Operand::from_bytes(vec![], ty); + let func = Operand::from_bytes(Box::default(), ty); return self.lower_call_and_args( func, iter::once(*callee).chain(args.iter().copied()), @@ -615,7 +615,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let callee_ty = self.expr_ty_after_adjustments(*callee); match &callee_ty.kind(Interner) { chalk_ir::TyKind::FnDef(..) => { - let func = Operand::from_bytes(vec![], callee_ty.clone()); + let func = Operand::from_bytes(Box::default(), callee_ty.clone()); self.lower_call_and_args( func, args.iter().copied(), @@ -1113,7 +1113,7 @@ impl<'ctx> MirLowerCtx<'ctx> { Some("start") => lp.take(), Some("end") => rp.take(), Some("exhausted") => { - Some(Operand::from_bytes(vec![0], TyBuilder::bool())) + Some(Operand::from_bytes(Box::new([0]), TyBuilder::bool())) } _ => None, }; @@ -1395,17 +1395,18 @@ impl<'ctx> MirLowerCtx<'ctx> { } fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result { - let size = self - .db - .layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner))? - .size - .bytes_usize(); - let bytes = match l { + let size = || { + self.db + .layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner)) + .map(|it| it.size.bytes_usize()) + }; + const USIZE_SIZE: usize = mem::size_of::(); + let bytes: Box<[_]> = match l { hir_def::hir::Literal::String(b) => { let b = b.as_bytes(); - let mut data = Vec::with_capacity(mem::size_of::() * 2); - data.extend(0usize.to_le_bytes()); - data.extend(b.len().to_le_bytes()); + let mut data = Box::new([0; { 2 * USIZE_SIZE }]); + data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes()); + data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes()); let mut mm = MemoryMap::default(); mm.insert(0, b.to_vec()); return Ok(Operand::from_concrete_const(data, mm, ty)); @@ -1413,28 +1414,28 @@ impl<'ctx> MirLowerCtx<'ctx> { hir_def::hir::Literal::CString(b) => { let bytes = b.iter().copied().chain(iter::once(0)).collect::>(); - let mut data = Vec::with_capacity(mem::size_of::() * 2); - data.extend(0usize.to_le_bytes()); - data.extend(bytes.len().to_le_bytes()); + let mut data = Box::new([0; { 2 * USIZE_SIZE }]); + data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes()); + data[USIZE_SIZE..].copy_from_slice(&bytes.len().to_le_bytes()); let mut mm = MemoryMap::default(); mm.insert(0, bytes); return Ok(Operand::from_concrete_const(data, mm, ty)); } hir_def::hir::Literal::ByteString(b) => { - let mut data = Vec::with_capacity(mem::size_of::() * 2); - data.extend(0usize.to_le_bytes()); - data.extend(b.len().to_le_bytes()); + let mut data = Box::new([0; { 2 * USIZE_SIZE }]); + data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes()); + data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes()); let mut mm = MemoryMap::default(); mm.insert(0, b.to_vec()); return Ok(Operand::from_concrete_const(data, mm, ty)); } - hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(), - hir_def::hir::Literal::Bool(b) => vec![*b as u8], - hir_def::hir::Literal::Int(it, _) => it.to_le_bytes()[0..size].into(), - hir_def::hir::Literal::Uint(it, _) => it.to_le_bytes()[0..size].into(), - hir_def::hir::Literal::Float(f, _) => match size { - 8 => f.into_f64().to_le_bytes().into(), - 4 => f.into_f32().to_le_bytes().into(), + hir_def::hir::Literal::Char(c) => Box::new(u32::from(*c).to_le_bytes()), + hir_def::hir::Literal::Bool(b) => Box::new([*b as u8]), + hir_def::hir::Literal::Int(it, _) => Box::from(&it.to_le_bytes()[0..size()?]), + hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]), + hir_def::hir::Literal::Float(f, _) => match size()? { + 8 => Box::new(f.into_f64().to_le_bytes()), + 4 => Box::new(f.into_f32().to_le_bytes()), _ => { return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes")) } @@ -1483,7 +1484,7 @@ impl<'ctx> MirLowerCtx<'ctx> { &mut self, prev_block: BasicBlockId, place: Place, - cv: Vec, + cv: Box<[u8]>, ty: Ty, span: MirSpan, ) -> Result<()> { diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 97ff65a455d3..98c2e7c63bc1 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -244,7 +244,7 @@ impl MirLowerCtx<'_> { ); } else { let c = Operand::from_concrete_const( - pattern_len.to_le_bytes().to_vec(), + pattern_len.to_le_bytes().into(), MemoryMap::default(), TyBuilder::usize(), ); From d40f1b1172b646d12a340a5b913a363678bc4cd6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 6 Jan 2024 17:52:47 +0100 Subject: [PATCH 133/257] Remove `Matrix.wildcard_row` It was only used to track types and relevancy, so may as well store that directly. --- .../rustc_pattern_analysis/src/usefulness.rs | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 68da13861aa8..7d4171be94a0 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -717,7 +717,7 @@ use std::fmt; use crate::constructor::{Constructor, ConstructorSet}; use crate::pat::{DeconstructedPat, WitnessPat}; -use crate::{Captures, MatchArm, MatchCtxt, TypeCx, TypedArena}; +use crate::{Captures, MatchArm, MatchCtxt, TypeCx}; use self::ValidityConstraint::*; @@ -984,11 +984,13 @@ struct Matrix<'p, Cx: TypeCx> { /// each column must have the same type. Each column corresponds to a place within the /// scrutinee. rows: Vec>, - /// Stores an extra fictitious row full of wildcards. Mostly used to keep track of the type of - /// each column. This must obey the same invariants as the real rows. - wildcard_row: PatStack<'p, Cx>, + /// Track the type of each column/place. + place_ty: SmallVec<[Cx::Ty; 2]>, /// Track for each column/place whether it contains a known valid value. place_validity: SmallVec<[ValidityConstraint; 2]>, + /// Track whether the virtual wildcard row used to compute exhaustiveness is relevant. See top + /// of the file for details on relevancy. + wildcard_row_is_relevant: bool, } impl<'p, Cx: TypeCx> Matrix<'p, Cx> { @@ -1007,17 +1009,15 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { /// Build a new matrix from an iterator of `MatchArm`s. fn new( - wildcard_arena: &'p TypedArena>, arms: &[MatchArm<'p, Cx>], scrut_ty: Cx::Ty, scrut_validity: ValidityConstraint, ) -> Self { - let wild_pattern = wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); - let wildcard_row = PatStack::from_pattern(wild_pattern); let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), - wildcard_row, + place_ty: smallvec![scrut_ty], place_validity: smallvec![scrut_validity], + wildcard_row_is_relevant: true, }; for (row_id, arm) in arms.iter().enumerate() { let v = MatrixRow { @@ -1032,10 +1032,10 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { } fn head_ty(&self) -> Option { - self.wildcard_row.head_opt().map(|pat| pat.ty()) + self.place_ty.first().copied() } fn column_count(&self) -> usize { - self.wildcard_row.len() + self.place_ty.len() } fn rows( @@ -1063,14 +1063,20 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { ctor: &Constructor, ctor_is_relevant: bool, ) -> Matrix<'p, Cx> { - let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor, ctor_is_relevant); - let new_validity = self.place_validity[0].specialize(ctor); - let new_place_validity = std::iter::repeat(new_validity) + let ctor_sub_tys = pcx.ctor_sub_tys(ctor); + let specialized_place_ty = + ctor_sub_tys.iter().chain(self.place_ty[1..].iter()).copied().collect(); + let ctor_sub_validity = self.place_validity[0].specialize(ctor); + let specialized_place_validity = std::iter::repeat(ctor_sub_validity) .take(ctor.arity(pcx)) .chain(self.place_validity[1..].iter().copied()) .collect(); - let mut matrix = - Matrix { rows: Vec::new(), wildcard_row, place_validity: new_place_validity }; + let mut matrix = Matrix { + rows: Vec::new(), + place_ty: specialized_place_ty, + place_validity: specialized_place_validity, + wildcard_row_is_relevant: self.wildcard_row_is_relevant && ctor_is_relevant, + }; for (i, row) in self.rows().enumerate() { if ctor.is_covered_by(pcx, row.head().ctor()) { let new_row = row.pop_head_constructor(pcx, ctor, ctor_is_relevant, i); @@ -1335,7 +1341,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( ) -> WitnessMatrix { debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count())); - if !matrix.wildcard_row.relevant && matrix.rows().all(|r| !r.pats.relevant) { + if !matrix.wildcard_row_is_relevant && matrix.rows().all(|r| !r.pats.relevant) { // Here we know that nothing will contribute further to exhaustiveness or usefulness. This // is purely an optimization: skipping this check doesn't affect correctness. See the top of // the file for details. @@ -1356,7 +1362,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( } // No (unguarded) rows, so the match is not exhaustive. We return a new witness unless // irrelevant. - return if matrix.wildcard_row.relevant { + return if matrix.wildcard_row_is_relevant { WitnessMatrix::unit_witness() } else { // We choose to not report anything here; see at the top for details. @@ -1466,7 +1472,7 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>( scrut_ty: Cx::Ty, scrut_validity: ValidityConstraint, ) -> UsefulnessReport<'p, Cx> { - let mut matrix = Matrix::new(cx.wildcard_arena, arms, scrut_ty, scrut_validity); + let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity); let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(cx, &mut matrix, true); let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column(); From 50b197c6ee0c42df56020bd4c5d8e393411fb8c0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 6 Jan 2024 17:58:57 +0100 Subject: [PATCH 134/257] Reuse `ctor_sub_tys` when we have one around --- compiler/rustc_pattern_analysis/src/lints.rs | 3 ++- compiler/rustc_pattern_analysis/src/pat.rs | 5 +++-- compiler/rustc_pattern_analysis/src/usefulness.rs | 9 ++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 83210a4a5562..f1237ecf83c6 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -83,8 +83,9 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> { (0..arity).map(|_| Self { patterns: Vec::new() }).collect(); let relevant_patterns = self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor())); + let ctor_sub_tys = pcx.ctor_sub_tys(ctor); for pat in relevant_patterns { - let specialized = pat.specialize(pcx, ctor); + let specialized = pat.specialize(pcx, ctor, ctor_sub_tys); for (subpat, column) in specialized.iter().zip(&mut specialized_columns) { if subpat.is_or_pat() { column.patterns.extend(subpat.flatten_or_pat()) diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index db41d2824a1e..4438d20a3579 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -81,10 +81,11 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> { &self, pcx: &PlaceCtxt<'_, 'p, Cx>, other_ctor: &Constructor, + ctor_sub_tys: &[Cx::Ty], ) -> SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]> { let wildcard_sub_tys = || { - let tys = pcx.ctor_sub_tys(other_ctor); - tys.iter() + ctor_sub_tys + .iter() .map(|ty| DeconstructedPat::wildcard(*ty)) .map(|pat| pcx.mcx.wildcard_arena.alloc(pat) as &_) .collect() diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 7d4171be94a0..16bf709881b6 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -874,11 +874,12 @@ impl<'p, Cx: TypeCx> PatStack<'p, Cx> { &self, pcx: &PlaceCtxt<'_, 'p, Cx>, ctor: &Constructor, + ctor_sub_tys: &[Cx::Ty], ctor_is_relevant: bool, ) -> PatStack<'p, Cx> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. - let mut new_pats = self.head().specialize(pcx, ctor); + let mut new_pats = self.head().specialize(pcx, ctor, ctor_sub_tys); new_pats.extend_from_slice(&self.pats[1..]); // `ctor` is relevant for this row if it is the actual constructor of this row, or if the // row has a wildcard and `ctor` is relevant for wildcards. @@ -950,11 +951,12 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> { &self, pcx: &PlaceCtxt<'_, 'p, Cx>, ctor: &Constructor, + ctor_sub_tys: &[Cx::Ty], ctor_is_relevant: bool, parent_row: usize, ) -> MatrixRow<'p, Cx> { MatrixRow { - pats: self.pats.pop_head_constructor(pcx, ctor, ctor_is_relevant), + pats: self.pats.pop_head_constructor(pcx, ctor, ctor_sub_tys, ctor_is_relevant), parent_row, is_under_guard: self.is_under_guard, useful: false, @@ -1079,7 +1081,8 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { }; for (i, row) in self.rows().enumerate() { if ctor.is_covered_by(pcx, row.head().ctor()) { - let new_row = row.pop_head_constructor(pcx, ctor, ctor_is_relevant, i); + let new_row = + row.pop_head_constructor(pcx, ctor, ctor_sub_tys, ctor_is_relevant, i); matrix.expand_and_push(new_row); } } From 30e17e377c32ff5998e7d6706c0b6ccfe025b5c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 6 Jan 2024 19:44:39 +0200 Subject: [PATCH 135/257] Fix panic on unaligned packed attribute --- crates/hir-def/src/data/adt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index b163112db918..a95b78614e82 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -128,7 +128,7 @@ fn parse_repr_tt(tt: &Subtree) -> Option { } else { 0 }; - let pack = Align::from_bytes(pack).unwrap(); + let pack = Align::from_bytes(pack).unwrap_or(Align::ONE); min_pack = Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack }); ReprFlags::empty() From 5e2b66fc9da7dadb40062a365ac540d4a031da25 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 6 Jan 2024 17:00:24 +0000 Subject: [PATCH 136/257] Don't populate yield and resume types after the fact --- .../src/transform/promote_consts.rs | 2 +- compiler/rustc_middle/src/mir/mod.rs | 29 +++-- compiler/rustc_middle/src/thir.rs | 2 - compiler/rustc_mir_build/src/build/mod.rs | 120 +++++++++--------- compiler/rustc_mir_build/src/build/scope.rs | 10 +- 5 files changed, 85 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 8b2ea2dc21dd..155cf4ff9e2c 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -969,7 +969,7 @@ pub fn promote_candidates<'tcx>( 0, vec![], body.span, - body.coroutine_kind(), + None, body.tainted_by_errors, ); promoted.phase = MirPhase::Analysis(AnalysisPhase::Initial); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 45dbfe6b8a7d..d426f6d8969a 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -263,6 +263,23 @@ pub struct CoroutineInfo<'tcx> { pub coroutine_kind: CoroutineKind, } +impl<'tcx> CoroutineInfo<'tcx> { + // Sets up `CoroutineInfo` for a pre-coroutine-transform MIR body. + pub fn initial( + coroutine_kind: CoroutineKind, + yield_ty: Ty<'tcx>, + resume_ty: Ty<'tcx>, + ) -> CoroutineInfo<'tcx> { + CoroutineInfo { + coroutine_kind, + yield_ty: Some(yield_ty), + resume_ty: Some(resume_ty), + coroutine_drop: None, + coroutine_layout: None, + } + } +} + /// The lowered representation of a single function. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct Body<'tcx> { @@ -367,7 +384,7 @@ impl<'tcx> Body<'tcx> { arg_count: usize, var_debug_info: Vec>, span: Span, - coroutine_kind: Option, + coroutine: Option>>, tainted_by_errors: Option, ) -> Self { // We need `arg_count` locals, and one for the return place. @@ -384,15 +401,7 @@ impl<'tcx> Body<'tcx> { source, basic_blocks: BasicBlocks::new(basic_blocks), source_scopes, - coroutine: coroutine_kind.map(|coroutine_kind| { - Box::new(CoroutineInfo { - yield_ty: None, - resume_ty: None, - coroutine_drop: None, - coroutine_layout: None, - coroutine_kind, - }) - }), + coroutine, local_decls, user_type_annotations, arg_count, diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 2b5983314eec..b4b8387c262b 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -86,8 +86,6 @@ macro_rules! thir_with_elements { } } -pub const UPVAR_ENV_PARAM: ParamId = ParamId::from_u32(0); - thir_with_elements! { body_type: BodyTy<'tcx>, diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index c4cade839478..b8d08319422d 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -9,7 +9,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{CoroutineKind, Node}; +use rustc_hir::Node; use rustc_index::bit_set::GrowableBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -177,7 +177,7 @@ struct Builder<'a, 'tcx> { check_overflow: bool, fn_span: Span, arg_count: usize, - coroutine_kind: Option, + coroutine: Option>>, /// The current set of scopes, updated as we traverse; /// see the `scope` module for more details. @@ -458,7 +458,6 @@ fn construct_fn<'tcx>( ) -> Body<'tcx> { let span = tcx.def_span(fn_def); let fn_id = tcx.local_def_id_to_hir_id(fn_def); - let coroutine_kind = tcx.coroutine_kind(fn_def); // The representation of thir for `-Zunpretty=thir-tree` relies on // the entry expression being the last element of `thir.exprs`. @@ -488,17 +487,15 @@ fn construct_fn<'tcx>( let arguments = &thir.params; - let (resume_ty, yield_ty, return_ty) = if coroutine_kind.is_some() { - let coroutine_ty = arguments[thir::UPVAR_ENV_PARAM].ty; - let coroutine_sig = match coroutine_ty.kind() { - ty::Coroutine(_, gen_args, ..) => gen_args.as_coroutine().sig(), - _ => { - span_bug!(span, "coroutine w/o coroutine type: {:?}", coroutine_ty) - } - }; - (Some(coroutine_sig.resume_ty), Some(coroutine_sig.yield_ty), coroutine_sig.return_ty) - } else { - (None, None, fn_sig.output()) + let return_ty = fn_sig.output(); + let coroutine = match tcx.type_of(fn_def).instantiate_identity().kind() { + ty::Coroutine(_, args) => Some(Box::new(CoroutineInfo::initial( + tcx.coroutine_kind(fn_def).unwrap(), + args.as_coroutine().yield_ty(), + args.as_coroutine().resume_ty(), + ))), + ty::Closure(..) | ty::FnDef(..) => None, + ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"), }; if let Some(custom_mir_attr) = @@ -529,7 +526,7 @@ fn construct_fn<'tcx>( safety, return_ty, return_ty_span, - coroutine_kind, + coroutine, ); let call_site_scope = @@ -563,11 +560,6 @@ fn construct_fn<'tcx>( None }; - if coroutine_kind.is_some() { - body.coroutine.as_mut().unwrap().yield_ty = yield_ty; - body.coroutine.as_mut().unwrap().resume_ty = resume_ty; - } - body } @@ -632,47 +624,62 @@ fn construct_const<'a, 'tcx>( fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -> Body<'_> { let span = tcx.def_span(def_id); let hir_id = tcx.local_def_id_to_hir_id(def_id); - let coroutine_kind = tcx.coroutine_kind(def_id); - let (inputs, output, resume_ty, yield_ty) = match tcx.def_kind(def_id) { + let (inputs, output, coroutine) = match tcx.def_kind(def_id) { DefKind::Const | DefKind::AssocConst | DefKind::AnonConst | DefKind::InlineConst - | DefKind::Static(_) => (vec![], tcx.type_of(def_id).instantiate_identity(), None, None), + | DefKind::Static(_) => (vec![], tcx.type_of(def_id).instantiate_identity(), None), DefKind::Ctor(..) | DefKind::Fn | DefKind::AssocFn => { let sig = tcx.liberate_late_bound_regions( def_id.to_def_id(), tcx.fn_sig(def_id).instantiate_identity(), ); - (sig.inputs().to_vec(), sig.output(), None, None) - } - DefKind::Closure if coroutine_kind.is_some() => { - let coroutine_ty = tcx.type_of(def_id).instantiate_identity(); - let ty::Coroutine(_, args) = coroutine_ty.kind() else { - bug!("expected type of coroutine-like closure to be a coroutine") - }; - let args = args.as_coroutine(); - let resume_ty = args.resume_ty(); - let yield_ty = args.yield_ty(); - let return_ty = args.return_ty(); - (vec![coroutine_ty, args.resume_ty()], return_ty, Some(resume_ty), Some(yield_ty)) + (sig.inputs().to_vec(), sig.output(), None) } DefKind::Closure => { let closure_ty = tcx.type_of(def_id).instantiate_identity(); - let ty::Closure(_, args) = closure_ty.kind() else { - bug!("expected type of closure to be a closure") - }; - let args = args.as_closure(); - let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), args.sig()); - let self_ty = match args.kind() { - ty::ClosureKind::Fn => Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, closure_ty), - ty::ClosureKind::FnMut => Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, closure_ty), - ty::ClosureKind::FnOnce => closure_ty, - }; - ([self_ty].into_iter().chain(sig.inputs().to_vec()).collect(), sig.output(), None, None) + match closure_ty.kind() { + ty::Closure(_, args) => { + let args = args.as_closure(); + let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), args.sig()); + let self_ty = match args.kind() { + ty::ClosureKind::Fn => { + Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, closure_ty) + } + ty::ClosureKind::FnMut => { + Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, closure_ty) + } + ty::ClosureKind::FnOnce => closure_ty, + }; + ( + [self_ty].into_iter().chain(sig.inputs().to_vec()).collect(), + sig.output(), + None, + ) + } + ty::Coroutine(_, args) => { + let args = args.as_coroutine(); + let resume_ty = args.resume_ty(); + let yield_ty = args.yield_ty(); + let return_ty = args.return_ty(); + ( + vec![closure_ty, args.resume_ty()], + return_ty, + Some(Box::new(CoroutineInfo::initial( + tcx.coroutine_kind(def_id).unwrap(), + yield_ty, + resume_ty, + ))), + ) + } + _ => { + span_bug!(span, "expected type of closure body to be a closure or coroutine"); + } + } } - dk => bug!("{:?} is not a body: {:?}", def_id, dk), + dk => span_bug!(span, "{:?} is not a body: {:?}", def_id, dk), }; let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }; @@ -696,7 +703,7 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) - cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); - let mut body = Body::new( + Body::new( MirSource::item(def_id.to_def_id()), cfg.basic_blocks, source_scopes, @@ -705,16 +712,9 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) - inputs.len(), vec![], span, - coroutine_kind, + coroutine, Some(guar), - ); - - body.coroutine.as_mut().map(|gen| { - gen.yield_ty = yield_ty; - gen.resume_ty = resume_ty; - }); - - body + ) } impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -728,7 +728,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { safety: Safety, return_ty: Ty<'tcx>, return_span: Span, - coroutine_kind: Option, + coroutine: Option>>, ) -> Builder<'a, 'tcx> { let tcx = infcx.tcx; let attrs = tcx.hir().attrs(hir_id); @@ -759,7 +759,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { cfg: CFG { basic_blocks: IndexVec::new() }, fn_span: span, arg_count, - coroutine_kind, + coroutine, scopes: scope::Scopes::new(), block_context: BlockContext::new(), source_scopes: IndexVec::new(), @@ -803,7 +803,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.arg_count, self.var_debug_info, self.fn_span, - self.coroutine_kind, + self.coroutine, None, ) } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 1a700ac7342e..48b237f3ae62 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -706,7 +706,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // If we are emitting a `drop` statement, we need to have the cached // diverge cleanup pads ready in case that drop panics. let needs_cleanup = self.scopes.scopes.last().is_some_and(|scope| scope.needs_cleanup()); - let is_coroutine = self.coroutine_kind.is_some(); + let is_coroutine = self.coroutine.is_some(); let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX }; let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes"); @@ -960,7 +960,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // path, we only need to invalidate the cache for drops that happen on // the unwind or coroutine drop paths. This means that for // non-coroutines we don't need to invalidate caches for `DropKind::Storage`. - let invalidate_caches = needs_drop || self.coroutine_kind.is_some(); + let invalidate_caches = needs_drop || self.coroutine.is_some(); for scope in self.scopes.scopes.iter_mut().rev() { if invalidate_caches { scope.invalidate_cache(); @@ -1073,7 +1073,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return cached_drop; } - let is_coroutine = self.coroutine_kind.is_some(); + let is_coroutine = self.coroutine.is_some(); for scope in &mut self.scopes.scopes[uncached_scope..=target] { for drop in &scope.drops { if is_coroutine || drop.kind == DropKind::Value { @@ -1318,7 +1318,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { blocks[ROOT_NODE] = continue_block; drops.build_mir::(&mut self.cfg, &mut blocks); - let is_coroutine = self.coroutine_kind.is_some(); + let is_coroutine = self.coroutine.is_some(); // Link the exit drop tree to unwind drop tree. if drops.drops.iter().any(|(drop, _)| drop.kind == DropKind::Value) { @@ -1355,7 +1355,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { /// Build the unwind and coroutine drop trees. pub(crate) fn build_drop_trees(&mut self) { - if self.coroutine_kind.is_some() { + if self.coroutine.is_some() { self.build_coroutine_drop_trees(); } else { Self::build_unwind_tree( From 6d8fb57d1a47f61c8a7a9d58cd46e06174dba50f Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sat, 23 Dec 2023 15:03:52 +0100 Subject: [PATCH 137/257] rustc_mir_transform: Enforce `rustc::potential_query_instability` lint Stop allowing `rustc::potential_query_instability` on all of rustc_mir_transform and instead allow it on a case-by-case basis if it is safe to do so. In this particular crate, all instances were safe to allow. --- compiler/rustc_mir_transform/src/const_prop_lint.rs | 2 ++ compiler/rustc_mir_transform/src/lib.rs | 1 - compiler/rustc_mir_transform/src/unreachable_prop.rs | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 99eecb567f27..d0bbca08a406 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -670,6 +670,8 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { // This loop can get very hot for some bodies: it check each local in each bb. // To avoid this quadratic behaviour, we only clear the locals that were modified inside // the current block. + // The order in which we remove consts does not matter. + #[allow(rustc::potential_query_instability)] for local in written_only_inside_own_block_locals.drain() { debug_assert_eq!( self.ecx.machine.can_const_prop[local], diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 164b6b9c4f5c..f5f51c0ec8ad 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,4 +1,3 @@ -#![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] #![feature(box_patterns)] diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index f12a6aa2429c..bff59aa60237 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -54,6 +54,8 @@ impl MirPass<'_> for UnreachablePropagation { patch.apply(body); // We do want do keep some unreachable blocks, but make them empty. + // The order in which we clear bb statements does not matter. + #[allow(rustc::potential_query_instability)] for bb in unreachable_blocks { body.basic_blocks_mut()[bb].statements.clear(); } From 735a6a4212eb658b59564bf5bdc3adc28afc6c75 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 16 Dec 2023 15:55:56 -0500 Subject: [PATCH 138/257] Run Miri and mir-opt tests without a target linker --- src/bootstrap/src/core/build_steps/compile.rs | 34 ++++++++++++++++--- src/bootstrap/src/core/build_steps/test.rs | 9 +++-- src/bootstrap/src/core/sanity.rs | 8 ++++- .../x86_64-gnu-tools/checktools.sh | 2 -- tests/mir-opt/remove_never_const.rs | 3 -- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d699c4fe536c..b240ab9ec6fc 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -46,6 +46,7 @@ pub struct Std { /// but we need to use the downloaded copy of std for linking to rustdoc. Allow this to be overriden by `builder.ensure` from other steps. force_recompile: bool, extra_rust_args: &'static [&'static str], + is_for_mir_opt_tests: bool, } impl Std { @@ -56,6 +57,7 @@ impl Std { crates: Default::default(), force_recompile: false, extra_rust_args: &[], + is_for_mir_opt_tests: false, } } @@ -66,6 +68,18 @@ impl Std { crates: Default::default(), force_recompile: true, extra_rust_args: &[], + is_for_mir_opt_tests: false, + } + } + + pub fn new_for_mir_opt_tests(compiler: Compiler, target: TargetSelection) -> Self { + Self { + target, + compiler, + crates: Default::default(), + force_recompile: false, + extra_rust_args: &[], + is_for_mir_opt_tests: true, } } @@ -80,6 +94,7 @@ impl Std { crates: Default::default(), force_recompile: false, extra_rust_args, + is_for_mir_opt_tests: false, } } } @@ -109,6 +124,7 @@ impl Step for Std { crates, force_recompile: false, extra_rust_args: &[], + is_for_mir_opt_tests: false, }); } @@ -206,11 +222,19 @@ impl Step for Std { } } - let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "build"); - std_cargo(builder, target, compiler.stage, &mut cargo); - for krate in &*self.crates { - cargo.arg("-p").arg(krate); - } + let mut cargo = if self.is_for_mir_opt_tests { + let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "rustc"); + cargo.arg("-p").arg("std").arg("--crate-type=lib"); + std_cargo(builder, target, compiler.stage, &mut cargo); + cargo + } else { + let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "build"); + std_cargo(builder, target, compiler.stage, &mut cargo); + for krate in &*self.crates { + cargo.arg("-p").arg(krate); + } + cargo + }; // See src/bootstrap/synthetic_targets.rs if target.is_synthetic() { diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 7f46726b9565..c5db8be959ba 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1611,7 +1611,12 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the .ensure(dist::DebuggerScripts { sysroot: builder.sysroot(compiler), host: target }); } - builder.ensure(compile::Std::new(compiler, target)); + if suite == "mir-opt" { + builder.ensure(compile::Std::new_for_mir_opt_tests(compiler, target)); + } else { + builder.ensure(compile::Std::new(compiler, target)); + } + // ensure that `libproc_macro` is available on the host. builder.ensure(compile::Std::new(compiler, compiler.host)); @@ -1619,7 +1624,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the builder.ensure(TestHelpers { target: compiler.host }); // As well as the target, except for plain wasm32, which can't build it - if !target.contains("wasm") || target.contains("emscripten") { + if suite != "mir-opt" && !target.contains("wasm") && !target.contains("emscripten") { builder.ensure(TestHelpers { target }); } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 82755f418000..5f1ca5de74af 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -62,9 +62,15 @@ impl Finder { } pub fn check(build: &mut Build) { - let skip_target_sanity = + let mut skip_target_sanity = env::var_os("BOOTSTRAP_SKIP_TARGET_SANITY").is_some_and(|s| s == "1" || s == "true"); + // Skip target sanity checks when we are doing anything with mir-opt tests or Miri + let skipped_paths = [OsStr::new("mir-opt"), OsStr::new("miri")]; + skip_target_sanity |= build.config.paths.iter().any(|path| { + path.components().any(|component| skipped_paths.contains(&component.as_os_str())) + }); + let path = env::var_os("PATH").unwrap_or_default(); // On Windows, quotes are invalid characters for filename paths, and if // one is present as part of the PATH then that can lead to the system diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index 205ee263217a..cc0c658aabd2 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -37,7 +37,6 @@ else fi # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. -export BOOTSTRAP_SKIP_TARGET_SANITY=1 # we don't need `cc` for these targets case $HOST_TARGET in x86_64-unknown-linux-gnu) # Only this branch runs in PR CI. @@ -62,4 +61,3 @@ case $HOST_TARGET in exit 1 ;; esac -unset BOOTSTRAP_SKIP_TARGET_SANITY diff --git a/tests/mir-opt/remove_never_const.rs b/tests/mir-opt/remove_never_const.rs index c144edaffafb..81562058d808 100644 --- a/tests/mir-opt/remove_never_const.rs +++ b/tests/mir-opt/remove_never_const.rs @@ -3,9 +3,6 @@ // consts in codegen. We also have tests for this that catches the error, see // tests/ui/consts/const-eval/index-out-of-bounds-never-type.rs. -// Force generation of optimized mir for functions that do not reach codegen. -// compile-flags: --emit mir,link - #![feature(never_type)] struct PrintName(T); From d17156a774420de86ec324083620802786a8d960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 6 Jan 2024 21:24:01 +0200 Subject: [PATCH 139/257] Add some repr(packed) tests --- crates/hir-ty/src/layout/tests.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index 9937113685ca..ef0be7ab2daf 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -220,6 +220,36 @@ fn recursive() { ); } +#[test] +fn repr_packed() { + size_and_align! { + #[repr(packed)] + struct Goal; + } + size_and_align! { + #[repr(packed(2))] + struct Goal; + } + size_and_align! { + #[repr(packed(4))] + struct Goal; + } + size_and_align! { + #[repr(packed)] + struct Goal(i32); + } + size_and_align! { + #[repr(packed(2))] + struct Goal(i32); + } + size_and_align! { + #[repr(packed(4))] + struct Goal(i32); + } + + check_size_and_align("#[repr(packed(5))] struct Goal(i32);", "", 4, 1); +} + #[test] fn generic() { size_and_align! { From 5ac0c143848435c444ffd2a4ce71c4b6a33fca47 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 6 Jan 2024 19:20:13 +0100 Subject: [PATCH 140/257] Remove some vec clones in const-eval --- crates/hir-ty/src/display.rs | 2 +- crates/hir-ty/src/infer/closure.rs | 4 +- crates/hir-ty/src/interner.rs | 32 ------- crates/hir-ty/src/lib.rs | 76 ++++++++++----- crates/hir-ty/src/mir/eval.rs | 143 +++++++++++++++++----------- crates/hir-ty/src/mir/eval/tests.rs | 4 +- crates/hir-ty/src/mir/lower.rs | 24 ++--- crates/hir/src/lib.rs | 4 +- 8 files changed, 161 insertions(+), 128 deletions(-) diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index d81926f7c976..4468b96b7764 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -515,7 +515,7 @@ fn render_const_scalar( TyKind::Dyn(_) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let ty_id = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); - let Ok(t) = memory_map.vtable.ty(ty_id) else { + let Ok(t) = memory_map.vtable_ty(ty_id) else { return f.write_str(""); }; let Ok(layout) = f.db.layout_of_ty(t.clone(), trait_env) else { diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index ac00b00fcd97..8a832b6b9042 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -1,6 +1,6 @@ //! Inference of closure parameter types based on the closure's expected type. -use std::{cmp, collections::HashMap, convert::Infallible, mem}; +use std::{cmp, convert::Infallible, mem}; use chalk_ir::{ cast::Cast, @@ -778,7 +778,7 @@ impl InferenceContext<'_> { fn minimize_captures(&mut self) { self.current_captures.sort_by_key(|it| it.place.projections.len()); - let mut hash_map = HashMap::::new(); + let mut hash_map = FxHashMap::::default(); let result = mem::take(&mut self.current_captures); for item in result { let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] }; diff --git a/crates/hir-ty/src/interner.rs b/crates/hir-ty/src/interner.rs index 7d3539edad1c..eb6296f7a04a 100644 --- a/crates/hir-ty/src/interner.rs +++ b/crates/hir-ty/src/interner.rs @@ -36,35 +36,6 @@ impl std::ops::Deref for InternedWrapper { } } -#[derive(Eq)] -pub struct PreHashedWrapper(T, u64); - -impl fmt::Debug for PreHashedWrapper { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -impl PartialEq for PreHashedWrapper { - fn eq(&self, other: &Self) -> bool { - self.1 == other.1 && self.0 == other.0 - } -} - -impl std::ops::Deref for PreHashedWrapper { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::hash::Hash for PreHashedWrapper { - fn hash(&self, state: &mut H) { - state.write_u64(self.1); - } -} - impl_internable!( InternedWrapper>, InternedWrapper>, @@ -76,7 +47,6 @@ impl_internable!( InternedWrapper>, InternedWrapper>, InternedWrapper>, - // InternedWrapper>, ); impl chalk_ir::interner::Interner for Interner { @@ -88,7 +58,6 @@ impl chalk_ir::interner::Interner for Interner { // We could do the following, but that saves "only" 20mb on self while increasing inferecene // time by ~2.5% // type InternedGoal = Interned>; - // type InternedGoal = Interned>>; type InternedGoal = Arc; type InternedGoals = Vec; type InternedSubstitution = Interned>>; @@ -97,7 +66,6 @@ impl chalk_ir::interner::Interner for Interner { type InternedQuantifiedWhereClauses = Interned>>; type InternedVariableKinds = Interned>>; type InternedCanonicalVarKinds = Interned>>; - // type InternedConstraints = SmallVec<[InEnvironment; 1]>; type InternedConstraints = Vec>; type InternedVariances = SmallVec<[Variance; 16]>; type DefId = InternId; diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 964ca7ce940e..793b52b49faa 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -37,8 +37,8 @@ mod tests; mod test_db; use std::{ - collections::{hash_map::Entry, HashMap}, - hash::Hash, + collections::hash_map::Entry, + hash::{BuildHasherDefault, Hash}, }; use chalk_ir::{ @@ -52,7 +52,7 @@ use hir_def::{hir::ExprId, type_ref::Rawness, GeneralConstId, TypeOrConstParamId use hir_expand::name; use la_arena::{Arena, Idx}; use mir::{MirEvalError, VTableMap}; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use syntax::ast::{make, ConstArg}; use traits::FnTrait; use triomphe::Arc; @@ -171,24 +171,45 @@ pub type Variances = chalk_ir::Variances; /// the necessary bits of memory of the const eval session to keep the constant /// meaningful. #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct MemoryMap { - pub memory: HashMap>, - pub vtable: VTableMap, +pub enum MemoryMap { + #[default] + Empty, + Simple(Box<[u8]>), + Complex(Box), } -impl MemoryMap { - fn insert(&mut self, addr: usize, x: Vec) { +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct ComplexMemoryMap { + memory: FxHashMap>, + vtable: VTableMap, +} + +impl ComplexMemoryMap { + fn insert(&mut self, addr: usize, val: Box<[u8]>) { match self.memory.entry(addr) { Entry::Occupied(mut e) => { - if e.get().len() < x.len() { - e.insert(x); + if e.get().len() < val.len() { + e.insert(val); } } Entry::Vacant(e) => { - e.insert(x); + e.insert(val); } } } +} + +impl MemoryMap { + pub fn vtable_ty(&self, id: usize) -> Result<&Ty, MirEvalError> { + match self { + MemoryMap::Empty | MemoryMap::Simple(_) => Err(MirEvalError::InvalidVTableId(id)), + MemoryMap::Complex(cm) => cm.vtable.ty(id), + } + } + + fn simple(v: Box<[u8]>) -> Self { + MemoryMap::Simple(v) + } /// This functions convert each address by a function `f` which gets the byte intervals and assign an address /// to them. It is useful when you want to load a constant with a memory map in a new memory. You can pass an @@ -196,22 +217,33 @@ impl MemoryMap { fn transform_addresses( &self, mut f: impl FnMut(&[u8], usize) -> Result, - ) -> Result, MirEvalError> { - self.memory - .iter() - .map(|x| { - let addr = *x.0; - let align = if addr == 0 { 64 } else { (addr - (addr & (addr - 1))).min(64) }; - Ok((addr, f(x.1, align)?)) - }) - .collect() + ) -> Result, MirEvalError> { + let mut transform = |(addr, val): (&usize, &Box<[u8]>)| { + let addr = *addr; + let align = if addr == 0 { 64 } else { (addr - (addr & (addr - 1))).min(64) }; + f(val, align).and_then(|it| Ok((addr, it))) + }; + match self { + MemoryMap::Empty => Ok(Default::default()), + MemoryMap::Simple(m) => transform((&0, m)).map(|(addr, val)| { + let mut map = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default()); + map.insert(addr, val); + map + }), + MemoryMap::Complex(cm) => cm.memory.iter().map(transform).collect(), + } } - fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> { + fn get(&self, addr: usize, size: usize) -> Option<&[u8]> { if size == 0 { Some(&[]) } else { - self.memory.get(&addr)?.get(0..size) + match self { + MemoryMap::Empty => Some(&[]), + MemoryMap::Simple(m) if addr == 0 => m.get(0..size), + MemoryMap::Simple(_) => None, + MemoryMap::Complex(cm) => cm.memory.get(&addr)?.get(0..size), + } } } } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 4c06566e7ce0..5650956d2f4c 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1,13 +1,6 @@ //! This module provides a MIR interpreter, which is used in const eval. -use std::{ - borrow::Cow, - cell::RefCell, - collections::{HashMap, HashSet}, - fmt::Write, - iter, mem, - ops::Range, -}; +use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range}; use base_db::{CrateId, FileId}; use chalk_ir::{cast::Cast, Mutability}; @@ -40,8 +33,8 @@ use crate::{ name, static_lifetime, traits::FnTrait, utils::{detect_variant_from_bytes, ClosureSubst}, - CallableDefId, ClosureId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution, - TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, + CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstScalar, FnDefId, Interner, MemoryMap, + Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; use super::{ @@ -98,6 +91,15 @@ impl VTableMap { let id = from_bytes!(usize, bytes); self.ty(id) } + + pub fn shrink_to_fit(&mut self) { + self.id_to_ty.shrink_to_fit(); + self.ty_to_id.shrink_to_fit(); + } + + fn is_empty(&self) -> bool { + self.id_to_ty.is_empty() && self.ty_to_id.is_empty() + } } #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -251,13 +253,6 @@ impl From for IntervalOrOwned { } impl IntervalOrOwned { - pub(crate) fn to_vec(self, memory: &Evaluator<'_>) -> Result> { - Ok(match self { - IntervalOrOwned::Owned(o) => o, - IntervalOrOwned::Borrowed(b) => b.get(memory)?.to_vec(), - }) - } - fn get<'a>(&'a self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { Ok(match self { IntervalOrOwned::Owned(o) => o, @@ -291,8 +286,8 @@ impl Address { } } - fn to_bytes(&self) -> Vec { - usize::to_le_bytes(self.to_usize()).to_vec() + fn to_bytes(&self) -> [u8; mem::size_of::()] { + usize::to_le_bytes(self.to_usize()) } fn to_usize(&self) -> usize { @@ -510,6 +505,20 @@ struct Locals { drop_flags: DropFlags, } +pub struct MirOutput { + stdout: Vec, + stderr: Vec, +} + +impl MirOutput { + pub fn stdout(&self) -> Cow<'_, str> { + String::from_utf8_lossy(&self.stdout) + } + pub fn stderr(&self) -> Cow<'_, str> { + String::from_utf8_lossy(&self.stderr) + } +} + pub fn interpret_mir( db: &dyn HirDatabase, body: Arc, @@ -520,7 +529,7 @@ pub fn interpret_mir( // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, trait_env: Option>, -) -> (Result, String, String) { +) -> (Result, MirOutput) { let ty = body.locals[return_slot()].ty.clone(); let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env); let it: Result = (|| { @@ -534,14 +543,17 @@ pub fn interpret_mir( &ty, &Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() }, )?; - memory_map.vtable = evaluator.vtable_map.clone(); - return Ok(intern_const_scalar(ConstScalar::Bytes(bytes.into(), memory_map), ty)); + let bytes = bytes.into(); + let memory_map = if memory_map.memory.is_empty() && evaluator.vtable_map.is_empty() { + MemoryMap::Empty + } else { + memory_map.vtable = mem::take(&mut evaluator.vtable_map); + memory_map.vtable.shrink_to_fit(); + MemoryMap::Complex(Box::new(memory_map)) + }; + return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); })(); - ( - it, - String::from_utf8_lossy(&evaluator.stdout).into_owned(), - String::from_utf8_lossy(&evaluator.stderr).into_owned(), - ) + (it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr }) } #[cfg(test)] @@ -563,7 +575,7 @@ impl Evaluator<'_> { code_stack: vec![], vtable_map: VTableMap::default(), thread_local_storage: TlsData::default(), - static_locations: HashMap::default(), + static_locations: Default::default(), db, random_state: oorandom::Rand64::new(0), trait_env: trait_env.unwrap_or_else(|| db.trait_environment_for_body(owner)), @@ -574,11 +586,11 @@ impl Evaluator<'_> { stack_depth_limit: 100, execution_limit: EXECUTION_LIMIT, memory_limit: 1000_000_000, // 2GB, 1GB for stack and 1GB for heap - layout_cache: RefCell::new(HashMap::default()), - projected_ty_cache: RefCell::new(HashMap::default()), - not_special_fn_cache: RefCell::new(HashSet::default()), - mir_or_dyn_index_cache: RefCell::new(HashMap::default()), - unused_locals_store: RefCell::new(HashMap::default()), + layout_cache: RefCell::new(Default::default()), + projected_ty_cache: RefCell::new(Default::default()), + not_special_fn_cache: RefCell::new(Default::default()), + mir_or_dyn_index_cache: RefCell::new(Default::default()), + unused_locals_store: RefCell::new(Default::default()), cached_ptr_size: match db.target_data_layout(crate_id) { Some(it) => it.pointer_size.bytes_usize(), None => 8, @@ -838,8 +850,8 @@ impl Evaluator<'_> { match &statement.kind { StatementKind::Assign(l, r) => { let addr = self.place_addr(l, &locals)?; - let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?; - self.write_memory(addr, &result)?; + let result = self.eval_rvalue(r, &mut locals)?; + self.copy_from_interval_or_owned(addr, result)?; locals .drop_flags .add_place(l.clone(), &locals.body.projection_store); @@ -1051,7 +1063,7 @@ impl Evaluator<'_> { Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), Rvalue::Ref(_, p) => { let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?; - let mut r = addr.to_bytes(); + let mut r = addr.to_bytes().to_vec(); if let Some(metadata) = metadata { r.extend(metadata.get(self)?); } @@ -1284,7 +1296,7 @@ impl Evaluator<'_> { not_supported!("unsized box initialization"); }; let addr = self.heap_allocate(size, align)?; - Owned(addr.to_bytes()) + Owned(addr.to_bytes().to_vec()) } Rvalue::CopyForDeref(_) => not_supported!("copy for deref"), Rvalue::Aggregate(kind, values) => { @@ -1716,7 +1728,18 @@ impl Evaluator<'_> { } let addr = self.heap_allocate(size, align)?; self.write_memory(addr, &v)?; - self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?; + self.patch_addresses( + &patch_map, + |bytes| match &memory_map { + MemoryMap::Empty | MemoryMap::Simple(_) => { + Err(MirEvalError::InvalidVTableId(from_bytes!(usize, bytes))) + } + MemoryMap::Complex(cm) => cm.vtable.ty_of_bytes(bytes), + }, + addr, + ty, + locals, + )?; Ok(Interval::new(addr, size)) } @@ -1768,6 +1791,13 @@ impl Evaluator<'_> { Ok(()) } + fn copy_from_interval_or_owned(&mut self, addr: Address, r: IntervalOrOwned) -> Result<()> { + match r { + IntervalOrOwned::Borrowed(r) => self.copy_from_interval(addr, r), + IntervalOrOwned::Owned(r) => self.write_memory(addr, &r), + } + } + fn copy_from_interval(&mut self, addr: Address, r: Interval) -> Result<()> { if r.size == 0 { return Ok(()); @@ -1888,13 +1918,18 @@ impl Evaluator<'_> { } } - fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals) -> Result { + fn create_memory_map( + &self, + bytes: &[u8], + ty: &Ty, + locals: &Locals, + ) -> Result { fn rec( this: &Evaluator<'_>, bytes: &[u8], ty: &Ty, locals: &Locals, - mm: &mut MemoryMap, + mm: &mut ComplexMemoryMap, ) -> Result<()> { match ty.kind(Interner) { TyKind::Ref(_, _, t) => { @@ -1904,7 +1939,7 @@ impl Evaluator<'_> { let addr_usize = from_bytes!(usize, bytes); mm.insert( addr_usize, - this.read_memory(Address::from_usize(addr_usize), size)?.to_vec(), + this.read_memory(Address::from_usize(addr_usize), size)?.into(), ) } None => { @@ -1930,7 +1965,7 @@ impl Evaluator<'_> { let size = element_size * count; let addr = Address::from_bytes(addr)?; let b = this.read_memory(addr, size)?; - mm.insert(addr.to_usize(), b.to_vec()); + mm.insert(addr.to_usize(), b.into()); if let Some(ty) = check_inner { for i in 0..count { let offset = element_size * i; @@ -2003,15 +2038,15 @@ impl Evaluator<'_> { } Ok(()) } - let mut mm = MemoryMap::default(); - rec(self, bytes, ty, locals, &mut mm)?; + let mut mm = ComplexMemoryMap::default(); + rec(&self, bytes, ty, locals, &mut mm)?; Ok(mm) } - fn patch_addresses( + fn patch_addresses<'vtable>( &mut self, - patch_map: &HashMap, - old_vtable: &VTableMap, + patch_map: &FxHashMap, + ty_of_bytes: impl Fn(&[u8]) -> Result<&'vtable Ty> + Copy, addr: Address, ty: &Ty, locals: &Locals, @@ -2038,7 +2073,7 @@ impl Evaluator<'_> { } } TyKind::Function(_) => { - let ty = old_vtable.ty_of_bytes(self.read_memory(addr, my_size)?)?.clone(); + let ty = ty_of_bytes(self.read_memory(addr, my_size)?)?.clone(); let new_id = self.vtable_map.id(ty); self.write_memory(addr, &new_id.to_le_bytes())?; } @@ -2049,7 +2084,7 @@ impl Evaluator<'_> { let ty = ty.clone().substitute(Interner, subst); self.patch_addresses( patch_map, - old_vtable, + ty_of_bytes, addr.offset(offset), &ty, locals, @@ -2071,7 +2106,7 @@ impl Evaluator<'_> { let ty = ty.clone().substitute(Interner, subst); self.patch_addresses( patch_map, - old_vtable, + ty_of_bytes, addr.offset(offset), &ty, locals, @@ -2084,7 +2119,7 @@ impl Evaluator<'_> { for (id, ty) in subst.iter(Interner).enumerate() { let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); - self.patch_addresses(patch_map, old_vtable, addr.offset(offset), ty, locals)?; + self.patch_addresses(patch_map, ty_of_bytes, addr.offset(offset), ty, locals)?; } } TyKind::Array(inner, len) => { @@ -2096,7 +2131,7 @@ impl Evaluator<'_> { for i in 0..len { self.patch_addresses( patch_map, - old_vtable, + ty_of_bytes, addr.offset(i * size), inner, locals, @@ -2167,7 +2202,7 @@ impl Evaluator<'_> { .map_err(|it| MirEvalError::MirLowerErrorForClosure(closure, it))?; let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some() { - closure_data.addr.to_bytes() + closure_data.addr.to_bytes().to_vec() } else { closure_data.get(self)?.to_owned() }; @@ -2553,7 +2588,7 @@ impl Evaluator<'_> { body, locals, drop_fn, - [IntervalOrOwned::Owned(addr.to_bytes())].into_iter(), + iter::once(IntervalOrOwned::Owned(addr.to_bytes().to_vec())), span, Interval { addr: Address::Invalid(0), size: 0 }, None, diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index b0f929279a5c..6552bf493377 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -31,9 +31,9 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr db.trait_environment(func_id.into()), ) .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; - let (result, stdout, stderr) = interpret_mir(db, body, false, None); + let (result, output) = interpret_mir(db, body, false, None); result?; - Ok((stdout, stderr)) + Ok((output.stdout().into_owned(), output.stderr().into_owned())) } fn check_pass(ra_fixture: &str) { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index a94da818b622..947fa3c21d6b 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1403,31 +1403,27 @@ impl<'ctx> MirLowerCtx<'ctx> { const USIZE_SIZE: usize = mem::size_of::(); let bytes: Box<[_]> = match l { hir_def::hir::Literal::String(b) => { - let b = b.as_bytes(); - let mut data = Box::new([0; { 2 * USIZE_SIZE }]); + let mut data = [0; { 2 * USIZE_SIZE }]; data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes()); data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes()); - let mut mm = MemoryMap::default(); - mm.insert(0, b.to_vec()); - return Ok(Operand::from_concrete_const(data, mm, ty)); + let mm = MemoryMap::simple(b.as_bytes().into()); + return Ok(Operand::from_concrete_const(Box::new(data), mm, ty)); } hir_def::hir::Literal::CString(b) => { - let bytes = b.iter().copied().chain(iter::once(0)).collect::>(); + let bytes = b.iter().copied().chain(iter::once(0)).collect::>(); - let mut data = Box::new([0; { 2 * USIZE_SIZE }]); + let mut data = [0; { 2 * USIZE_SIZE }]; data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes()); data[USIZE_SIZE..].copy_from_slice(&bytes.len().to_le_bytes()); - let mut mm = MemoryMap::default(); - mm.insert(0, bytes); - return Ok(Operand::from_concrete_const(data, mm, ty)); + let mm = MemoryMap::simple(bytes); + return Ok(Operand::from_concrete_const(Box::new(data), mm, ty)); } hir_def::hir::Literal::ByteString(b) => { - let mut data = Box::new([0; { 2 * USIZE_SIZE }]); + let mut data = [0; { 2 * USIZE_SIZE }]; data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes()); data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes()); - let mut mm = MemoryMap::default(); - mm.insert(0, b.to_vec()); - return Ok(Operand::from_concrete_const(data, mm, ty)); + let mm = MemoryMap::simple(b.clone()); + return Ok(Operand::from_concrete_const(Box::new(data), mm, ty)); } hir_def::hir::Literal::Char(c) => Box::new(u32::from(*c).to_le_bytes()), hir_def::hir::Literal::Bool(b) => Box::new([*b as u8]), diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 4e4b758264f4..49f599a67a1a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2183,7 +2183,7 @@ impl Function { return r; } }; - let (result, stdout, stderr) = interpret_mir(db, body, false, None); + let (result, output) = interpret_mir(db, body, false, None); let mut text = match result { Ok(_) => "pass".to_string(), Err(e) => { @@ -2192,10 +2192,12 @@ impl Function { r } }; + let stdout = output.stdout().into_owned(); if !stdout.is_empty() { text += "\n--------- stdout ---------\n"; text += &stdout; } + let stderr = output.stdout().into_owned(); if !stderr.is_empty() { text += "\n--------- stderr ---------\n"; text += &stderr; From 476e10e961e010932a995637097a20bd282a78ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 7 Jan 2024 00:17:48 +0100 Subject: [PATCH 141/257] remove redundant clones --- crates/hir-def/src/body/lower.rs | 2 +- crates/hir-def/src/nameres/collector.rs | 2 +- crates/hir-ty/src/display.rs | 2 +- crates/hir-ty/src/infer.rs | 2 +- crates/hir-ty/src/infer/closure.rs | 6 +++--- crates/hir-ty/src/infer/expr.rs | 2 +- crates/hir-ty/src/infer/pat.rs | 2 +- crates/hir-ty/src/layout.rs | 16 ++++++++-------- crates/hir-ty/src/method_resolution.rs | 2 +- crates/hir-ty/src/mir/eval.rs | 2 +- crates/hir/src/lib.rs | 2 +- crates/hir/src/semantics.rs | 2 +- crates/ide-assists/src/handlers/bool_to_enum.rs | 4 ++-- .../convert_tuple_return_type_to_struct.rs | 2 +- .../src/handlers/generate_delegate_methods.rs | 2 +- .../src/handlers/generate_function.rs | 2 +- .../src/handlers/generate_mut_trait_impl.rs | 2 +- crates/ide-db/src/items_locator.rs | 2 +- crates/ide-db/src/path_transform.rs | 2 +- crates/ide-db/src/search.rs | 2 +- crates/ide-db/src/source_change.rs | 4 ++-- .../src/handlers/unresolved_method.rs | 2 +- crates/project-model/src/cargo_workspace.rs | 2 +- 23 files changed, 34 insertions(+), 34 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index bc4da360c5a2..9e4542f7fca0 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -1610,7 +1610,7 @@ impl ExprCollector<'_> { |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))), |name, span| { if let Some(span) = span { - mappings.push((span, name.clone())) + mappings.push((span, name)) } }, ), diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 3763bfcbcfaf..a18ac4b28c4e 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -1397,7 +1397,7 @@ impl DefCollector<'_> { always!(krate == loc.def.krate); DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate) } - _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()), + _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()), }; self.def_map.diagnostics.push(diag); diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 4468b96b7764..23d951542214 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -609,7 +609,7 @@ fn render_const_scalar( } hir_def::AdtId::EnumId(e) => { let Some((var_id, var_layout)) = - detect_variant_from_bytes(&layout, f.db, trait_env.clone(), b, e) + detect_variant_from_bytes(&layout, f.db, trait_env, b, e) else { return f.write_str(""); }; diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 63edd0f824f4..a78e3e7dc251 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -738,7 +738,7 @@ impl<'a> InferenceContext<'a> { result.tuple_field_access_types = tuple_field_accesses_rev .into_iter() .enumerate() - .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst.clone()))) + .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst))) .collect(); result } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 8a832b6b9042..118b9c0149f6 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -130,7 +130,7 @@ impl HirPlace { ctx.owner.module(ctx.db.upcast()).krate(), ); } - ty.clone() + ty } fn capture_kind_of_truncated_place( @@ -245,7 +245,7 @@ pub(crate) struct CapturedItemWithoutTy { impl CapturedItemWithoutTy { fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem { - let ty = self.place.ty(ctx).clone(); + let ty = self.place.ty(ctx); let ty = match &self.kind { CaptureKind::ByValue => ty, CaptureKind::ByRef(bk) => { @@ -396,7 +396,7 @@ impl InferenceContext<'_> { fn consume_place(&mut self, place: HirPlace, span: MirSpan) { if self.is_upvar(&place) { - let ty = place.ty(self).clone(); + let ty = place.ty(self); let kind = if self.is_ty_copy(ty) { CaptureKind::ByRef(BorrowKind::Shared) } else { diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 123a9075683b..db631c8517c1 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -978,7 +978,7 @@ impl InferenceContext<'_> { .push(callee_ty.clone()) .push(TyBuilder::tuple_with(params.iter().cloned())) .build(); - self.write_method_resolution(tgt_expr, func, subst.clone()); + self.write_method_resolution(tgt_expr, func, subst); } } diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index acdb540289d7..3fdd76f7cd4c 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -336,7 +336,7 @@ impl InferenceContext<'_> { &Pat::Lit(expr) => { // Don't emit type mismatches again, the expression lowering already did that. let ty = self.infer_lit_pat(expr, &expected); - self.write_pat_ty(pat, ty.clone()); + self.write_pat_ty(pat, ty); return self.pat_ty_after_adjustment(pat); } Pat::Box { inner } => match self.resolve_boxed_box() { diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index bfc4f1383ec6..68619bb8b18c 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -164,7 +164,7 @@ fn layout_of_simd_ty( }; // Compute the ABI of the element type: - let e_ly = db.layout_of_ty(e_ty, env.clone())?; + let e_ly = db.layout_of_ty(e_ty, env)?; let Abi::Scalar(e_abi) = e_ly.abi else { return Err(LayoutError::Unknown); }; @@ -204,17 +204,17 @@ pub fn layout_of_ty_query( }; let cx = LayoutCx { target: &target }; let dl = &*cx.current_data_layout(); - let ty = normalize(db, trait_env.clone(), ty.clone()); + let ty = normalize(db, trait_env.clone(), ty); let result = match ty.kind(Interner) { TyKind::Adt(AdtId(def), subst) => { if let hir_def::AdtId::StructId(s) = def { let data = db.struct_data(*s); let repr = data.repr.unwrap_or_default(); if repr.simd() { - return layout_of_simd_ty(db, *s, subst, trait_env.clone(), &target); + return layout_of_simd_ty(db, *s, subst, trait_env, &target); } }; - return db.layout_of_adt(*def, subst.clone(), trait_env.clone()); + return db.layout_of_adt(*def, subst.clone(), trait_env); } TyKind::Scalar(s) => match s { chalk_ir::Scalar::Bool => Layout::scalar( @@ -280,7 +280,7 @@ pub fn layout_of_ty_query( } TyKind::Array(element, count) => { let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64; - let element = db.layout_of_ty(element.clone(), trait_env.clone())?; + let element = db.layout_of_ty(element.clone(), trait_env)?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) { @@ -303,7 +303,7 @@ pub fn layout_of_ty_query( } } TyKind::Slice(element) => { - let element = db.layout_of_ty(element.clone(), trait_env.clone())?; + let element = db.layout_of_ty(element.clone(), trait_env)?; Layout { variants: Variants::Single { index: struct_variant_idx() }, fields: FieldsShape::Array { stride: element.size, count: 0 }, @@ -345,7 +345,7 @@ pub fn layout_of_ty_query( })) .intern(Interner); } - unsized_part = normalize(db, trait_env.clone(), unsized_part); + unsized_part = normalize(db, trait_env, unsized_part); let metadata = match unsized_part.kind(Interner) { TyKind::Slice(_) | TyKind::Str => { scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false)) @@ -384,7 +384,7 @@ pub fn layout_of_ty_query( match impl_trait_id { crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { let infer = db.infer(func.into()); - return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env.clone()); + return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 041d61c1b153..33619edfee93 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -1350,7 +1350,7 @@ pub(crate) fn resolve_indexing_op( ty: Canonical, index_trait: TraitId, ) -> Option { - let mut table = InferenceTable::new(db, env.clone()); + let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); let deref_chain = autoderef_method_receiver(&mut table, ty); for (ty, adj) in deref_chain { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 5650956d2f4c..c16d303366e1 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1533,7 +1533,7 @@ impl Evaluator<'_> { } }, TyKind::Dyn(_) => { - let vtable = self.vtable_map.id(current_ty.clone()); + let vtable = self.vtable_map.id(current_ty); let mut r = Vec::with_capacity(16); let addr = addr.get(self)?; r.extend(addr.iter().copied()); diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 49f599a67a1a..2428783161fd 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1093,7 +1093,7 @@ impl Field { pub fn layout(&self, db: &dyn HirDatabase) -> Result { db.layout_of_ty( - self.ty(db).ty.clone(), + self.ty(db).ty, db.trait_environment(match hir_def::VariantId::from(self.parent) { hir_def::VariantId::EnumVariantId(id) => GenericDefId::EnumVariantId(id), hir_def::VariantId::StructId(id) => GenericDefId::AdtId(id.into()), diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index a82ae308fae6..f51fe80931c6 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -428,7 +428,7 @@ impl<'db> SemanticsImpl<'db> { if let Some(original_string) = ast::String::cast(original_token.clone()) { if let Some(quote) = original_string.open_quote_text_range() { return self - .descend_into_macros(DescendPreference::SameText, original_token.clone()) + .descend_into_macros(DescendPreference::SameText, original_token) .into_iter() .find_map(|token| { self.resolve_offset_in_format_args( diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs index b7b00e7ed068..dc1952c3ff3c 100644 --- a/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -301,7 +301,7 @@ fn replace_usages( // add imports across modules where needed if let Some((import_scope, path)) = import_data { - let scope = match import_scope.clone() { + let scope = match import_scope { ImportScope::File(it) => ImportScope::File(edit.make_mut(it)), ImportScope::Module(it) => ImportScope::Module(edit.make_mut(it)), ImportScope::Block(it) => ImportScope::Block(edit.make_mut(it)), @@ -329,7 +329,7 @@ fn augment_references_with_imports( references .into_iter() .filter_map(|FileReference { range, name, .. }| { - let name = name.clone().into_name_like()?; + let name = name.into_name_like()?; ctx.sema.scope(name.syntax()).map(|scope| (range, name, scope.module())) }) .map(|(range, name, ref_module)| { diff --git a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 79b46d66121e..41366658a74b 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -190,7 +190,7 @@ fn augment_references_with_imports( ctx.sema.scope(name.syntax()).map(|scope| (name, scope.module())) }) .map(|(name, ref_module)| { - let new_name = edit.make_mut(name.clone()); + let new_name = edit.make_mut(name); // if the referenced module is not the same as the target one and has not been seen before, add an import let import_data = if ref_module.nearest_non_block_module(ctx.db()) != *target_module diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index db1e0ceaec1e..dc02aaf9af68 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -147,7 +147,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' None => { let name = &strukt_name.to_string(); let params = strukt.generic_param_list(); - let ty_params = params.clone(); + let ty_params = params; let where_clause = strukt.where_clause(); let impl_def = make::impl_( diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 5bb200e84a49..50528e1caaac 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -432,7 +432,7 @@ fn get_fn_target( } None => next_space_for_fn_after_call_site(ast::CallableExpr::Call(call))?, }; - Some((target.clone(), file)) + Some((target, file)) } fn get_method_target( diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index cb8ef395650b..e15cc8abe9a0 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -47,7 +47,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> let impl_def = ctx.find_node_at_offset::()?.clone_for_update(); let trait_ = impl_def.trait_()?; - if let ast::Type::PathType(trait_path) = trait_.clone() { + if let ast::Type::PathType(trait_path) = trait_ { let trait_type = ctx.sema.resolve_trait(&trait_path.path()?)?; let scope = ctx.sema.scope(trait_path.syntax())?; if trait_type != FamousDefs(&ctx.sema, scope.krate()).core_convert_Index()? { diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs index a61acc5dd5a6..432f1d745d20 100644 --- a/crates/ide-db/src/items_locator.rs +++ b/crates/ide-db/src/items_locator.rs @@ -55,7 +55,7 @@ pub fn items_with_name<'a>( local_query.fuzzy(); local_query.assoc_search_mode(assoc_item_search); - let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) + let mut external_query = import_map::Query::new(fuzzy_search_string) .fuzzy() .assoc_search_mode(assoc_item_search); diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 8c1a6e6e40b8..edfeddc1bc9c 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -159,7 +159,7 @@ impl<'a> PathTransform<'a> { .for_each(|(k, v)| match (k.split(db), v) { (Either::Right(k), Some(TypeOrConst::Either(v))) => { if let Some(ty) = v.ty() { - type_substs.insert(k, ty.clone()); + type_substs.insert(k, ty); } } (Either::Right(k), None) => { diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index dbef36026822..a40dd2692cfd 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -539,7 +539,7 @@ impl<'a> FindUsages<'a> { tree.token_at_offset(offset).into_iter().for_each(|token| { let Some(str_token) = ast::String::cast(token.clone()) else { return }; if let Some((range, nameres)) = - sema.check_for_format_args_template(token.clone(), offset) + sema.check_for_format_args_template(token, offset) { if self.found_format_args_ref(file_id, range, str_token, nameres, sink) { return; diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs index c7188f1f7943..73be6a4071e4 100644 --- a/crates/ide-db/src/source_change.rs +++ b/crates/ide-db/src/source_change.rs @@ -341,13 +341,13 @@ impl SourceChangeBuilder { /// Adds a tabstop snippet to place the cursor before `token` pub fn add_tabstop_before_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { assert!(token.parent().is_some()); - self.add_snippet(PlaceSnippet::Before(token.clone().into())); + self.add_snippet(PlaceSnippet::Before(token.into())); } /// Adds a tabstop snippet to place the cursor after `token` pub fn add_tabstop_after_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { assert!(token.parent().is_some()); - self.add_snippet(PlaceSnippet::After(token.clone().into())); + self.add_snippet(PlaceSnippet::After(token.into())); } /// Adds a snippet to move the cursor selected over `node` diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 60a45a05a4a1..41fb67290852 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -160,7 +160,7 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) - // if receiver should be pass as first arg in the assoc func, // we could omit generic parameters cause compiler can deduce it automatically if !need_to_take_receiver_as_first_arg && !generic_parameters.is_empty() { - let generic_parameters = generic_parameters.join(", ").to_string(); + let generic_parameters = generic_parameters.join(", "); receiver_type_adt_name = format!("{}::<{}>", receiver_type_adt_name, generic_parameters); } diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index d89c4598afc7..bc1fcd08e20c 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -274,7 +274,7 @@ impl CargoWorkspace { other_options.append( &mut targets .into_iter() - .flat_map(|target| ["--filter-platform".to_owned().to_string(), target]) + .flat_map(|target| ["--filter-platform".to_owned(), target]) .collect(), ); } From b22bd36f6e9cebd23eb09f6fdc5d89c9cfe44453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 7 Jan 2024 00:26:49 +0100 Subject: [PATCH 142/257] remove more redundant clones manually --- crates/hir-ty/src/infer/pat.rs | 1 - crates/hir/src/lib.rs | 1 - crates/ide/src/goto_definition.rs | 2 +- crates/ide/src/view_memory_layout.rs | 4 ++-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 3fdd76f7cd4c..1bf8babe8365 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -233,7 +233,6 @@ impl InferenceContext<'_> { }; let mut expectations_iter = expectations .iter() - .cloned() .map(|a| a.assert_ty_ref(Interner).clone()) .chain(repeat_with(|| self.table.new_type_var())); diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 2428783161fd..fd843945dfa4 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -3673,7 +3673,6 @@ impl Closure { let (captures, _) = infer.closure_info(&self.id); captures .iter() - .cloned() .map(|capture| Type { env: db.trait_environment_for_body(owner), ty: capture.ty(&self.subst), diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index e0beba8fb380..d64295bdd690 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -79,7 +79,7 @@ pub(crate) fn goto_definition( return Some(vec![x]); } - if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.clone()) { + if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token) { return Some(vec![x]); } } diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs index 3802978f4941..53f998e5457d 100644 --- a/crates/ide/src/view_memory_layout.rs +++ b/crates/ide/src/view_memory_layout.rs @@ -69,7 +69,7 @@ impl FieldOrTupleIdx { .as_str() .map(|s| s.to_owned()) .unwrap_or_else(|| format!(".{}", f.name(db).as_tuple_index().unwrap())), - FieldOrTupleIdx::TupleIdx(i) => format!(".{i}").to_owned(), + FieldOrTupleIdx::TupleIdx(i) => format!(".{i}"), } } } @@ -203,7 +203,7 @@ pub(crate) fn view_memory_layout( let mut nodes = vec![MemoryLayoutNode { item_name, - typename: typename.clone(), + typename, size: layout.size(), offset: 0, alignment: layout.align(), From 196650dfafd6be76c63100e03e53256ad62c15fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 7 Jan 2024 01:10:12 +0100 Subject: [PATCH 143/257] don't to_string() format args --- crates/hir-ty/src/mir/eval.rs | 2 +- crates/hir-ty/src/mir/lower.rs | 2 +- .../src/handlers/generate_delegate_trait.rs | 16 +++++----------- .../src/handlers/generate_mut_trait_impl.rs | 2 +- .../src/handlers/generate_trait_from_impl.rs | 10 +++------- .../handlers/trait_impl_redundant_assoc_item.rs | 4 ++-- 6 files changed, 13 insertions(+), 23 deletions(-) diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index c16d303366e1..16075d907343 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -386,7 +386,7 @@ impl MirEvalError { write!( f, "Layout for type `{}` is not available due {err:?}", - ty.display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string() + ty.display(db).with_closure_style(ClosureStyle::ClosureWithId) )?; } MirEvalError::MirLowerError(func, err) => { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 947fa3c21d6b..817239ae761b 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -177,7 +177,7 @@ impl MirLowerError { )?; writeln!(f, "Provided args: [")?; for g in subst.iter(Interner) { - write!(f, " {},", g.display(db).to_string())?; + write!(f, " {},", g.display(db))?; } writeln!(f, "]")?; } diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 7731addcddf2..339c3ac71ecd 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -266,11 +266,8 @@ fn generate_impl( .clone_for_update(); // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths - let qualified_path_type = make::path_from_text(&format!( - "<{} as {}>", - field_ty.to_string(), - delegate.trait_()?.to_string() - )); + let qualified_path_type = + make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?)); let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); match bound_def.assoc_item_list() { @@ -373,11 +370,8 @@ fn generate_impl( .clone_for_update(); // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths - let qualified_path_type = make::path_from_text(&format!( - "<{} as {}>", - field_ty.to_string(), - delegate.trait_()?.to_string() - )); + let qualified_path_type = + make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?)); // 4) Transform associated items in delegte trait impl let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); @@ -759,7 +753,7 @@ fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option ast::Path { - make::path_from_text(&format!("{}::{}", qual_path_ty.to_string(), path_expr_seg.to_string())) + make::path_from_text(&format!("{}::{}", qual_path_ty, path_expr_seg)) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index e15cc8abe9a0..d90d366ffe4c 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -105,7 +105,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> "Generate `IndexMut` impl from this `Index` trait", target, |edit| { - edit.insert(target.start(), format!("$0{}\n\n", impl_def.to_string())); + edit.insert(target.start(), format!("$0{}\n\n", impl_def)); }, ) } diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 0f67380d12b6..315b6487b514 100644 --- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -128,7 +128,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ builder.replace_snippet( snippet_cap, impl_name.syntax().text_range(), - format!("${{0:TraitName}}{} for {}", arg_list, impl_name.to_string()), + format!("${{0:TraitName}}{} for {}", arg_list, impl_name), ); // Insert trait before TraitImpl @@ -144,17 +144,13 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ } else { builder.replace( impl_name.syntax().text_range(), - format!("NewTrait{} for {}", arg_list, impl_name.to_string()), + format!("NewTrait{} for {}", arg_list, impl_name), ); // Insert trait before TraitImpl builder.insert( impl_ast.syntax().text_range().start(), - format!( - "{}\n\n{}", - trait_ast.to_string(), - IndentLevel::from_node(impl_ast.syntax()) - ), + format!("{}\n\n{}", trait_ast, IndentLevel::from_node(impl_ast.syntax())), ); } diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index 62260725138a..6ecfd55ea024 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -32,7 +32,7 @@ pub(crate) fn trait_impl_redundant_assoc_item( .source(db) .map(|it| it.syntax().value.text_range()) .unwrap_or(default_range), - format!("\n {};", function.display(db).to_string()), + format!("\n {};", function.display(db)), ) } hir::AssocItem::Const(id) => { @@ -43,7 +43,7 @@ pub(crate) fn trait_impl_redundant_assoc_item( .source(db) .map(|it| it.syntax().value.text_range()) .unwrap_or(default_range), - format!("\n {};", constant.display(db).to_string()), + format!("\n {};", constant.display(db)), ) } hir::AssocItem::TypeAlias(id) => { From 3fb2cd200202bd3a7dfec892d85f96e359990874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 7 Jan 2024 01:20:20 +0100 Subject: [PATCH 144/257] autofix remaining perf findings --- crates/hir-ty/src/mir/lower.rs | 4 ++-- crates/hir/src/lib.rs | 4 ++-- .../src/handlers/convert_nested_function_to_closure.rs | 4 ++-- crates/ide-assists/src/handlers/extract_variable.rs | 2 +- crates/ide-assists/src/handlers/remove_parentheses.rs | 2 +- crates/ide-diagnostics/src/tests.rs | 5 +++-- crates/project-model/src/workspace.rs | 2 +- 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 817239ae761b..c02c5ef8767f 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -2070,8 +2070,8 @@ pub fn mir_body_for_closure_query( prev_projs .lookup(&store) .iter() - .cloned() - .skip(it.0.place.projections.len()), + .skip(it.0.place.projections.len()) + .cloned(), ); p.projection = store.intern(next_projs.into()); } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index fd843945dfa4..0266915c39b6 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1854,7 +1854,7 @@ impl DefWithBody { let local = Local { parent: self.into(), binding_id }; match (need_mut, local.is_mut(db)) { (mir::MutabilityReason::Unused, _) => { - let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with("_")); + let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with('_')); if !should_ignore { acc.push(UnusedVariable { local }.into()) } @@ -1879,7 +1879,7 @@ impl DefWithBody { } (mir::MutabilityReason::Not, true) => { if !infer.mutated_bindings_in_closure.contains(&binding_id) { - let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with("_")); + let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with('_')); if !should_ignore { acc.push(UnusedMut { local }.into()) } diff --git a/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs index 399f87c8f509..c30f3e1c3b2b 100644 --- a/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs +++ b/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs @@ -49,8 +49,8 @@ pub(crate) fn convert_nested_function_to_closure( target, |edit| { let params = ¶m_list.syntax().text().to_string(); - let params = params.strip_prefix("(").unwrap_or(params); - let params = params.strip_suffix(")").unwrap_or(params); + let params = params.strip_prefix('(').unwrap_or(params); + let params = params.strip_suffix(')').unwrap_or(params); let mut body = body.to_string(); if !has_semicolon(&function) { diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index 874b81d3b637..0b3bd0bed6ed 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs @@ -112,7 +112,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let insert_place = edit.make_syntax_mut(place); // Adjust ws to insert depending on if this is all inline or on separate lines - let trailing_ws = if prev_ws.is_some_and(|it| it.text().starts_with("\n")) { + let trailing_ws = if prev_ws.is_some_and(|it| it.text().starts_with('\n')) { format!("\n{indent_to}") } else { format!(" ") diff --git a/crates/ide-assists/src/handlers/remove_parentheses.rs b/crates/ide-assists/src/handlers/remove_parentheses.rs index 0281b29cd427..99c55e9ff7c0 100644 --- a/crates/ide-assists/src/handlers/remove_parentheses.rs +++ b/crates/ide-assists/src/handlers/remove_parentheses.rs @@ -43,7 +43,7 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> let prev_token = parens.syntax().first_token().and_then(|it| it.prev_token()); let need_to_add_ws = match prev_token { Some(it) => { - let tokens = vec![T![&], T![!], T!['('], T!['['], T!['{']]; + let tokens = [T![&], T![!], T!['('], T!['['], T!['{']]; it.kind() != SyntaxKind::WHITESPACE && !tokens.contains(&it.kind()) } None => false, diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index 9dc5ebbd6a67..742db32564de 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -42,8 +42,9 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id) .pop() .expect("no diagnostics"); - let fix = - &diagnostic.fixes.expect(&format!("{:?} diagnostic misses fixes", diagnostic.code))[nth]; + let fix = &diagnostic + .fixes + .unwrap_or_else(|| panic!("{:?} diagnostic misses fixes", diagnostic.code))[nth]; let actual = { let source_change = fix.source_change.as_ref().unwrap(); let file_id = *source_change.source_file_edits.keys().next().unwrap(); diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 4057493fa3a6..00cc7c30ee3e 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -1277,7 +1277,7 @@ fn add_target_crate_root( inject_cargo_env(pkg, &mut env); if let Ok(cname) = String::from_str(cargo_name) { // CARGO_CRATE_NAME is the name of the Cargo target with - converted to _, such as the name of the library, binary, example, integration test, or benchmark. - env.set("CARGO_CRATE_NAME", cname.replace("-", "_")); + env.set("CARGO_CRATE_NAME", cname.replace('-', "_")); } if let Some(envs) = build_data.map(|it| &it.envs) { From 41eb9a49aff07255f0cc28f6e435527a4435714a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 6 Jan 2024 22:17:16 +0000 Subject: [PATCH 145/257] Skip threading over no-op SetDiscriminant. --- .../rustc_mir_transform/src/jump_threading.rs | 22 +++++- .../set_no_discriminant.f.JumpThreading.diff | 26 +++++++ ...no_discriminant.generic.JumpThreading.diff | 26 +++++++ tests/mir-opt/set_no_discriminant.rs | 78 +++++++++++++++++++ 4 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 tests/mir-opt/set_no_discriminant.f.JumpThreading.diff create mode 100644 tests/mir-opt/set_no_discriminant.generic.JumpThreading.diff create mode 100644 tests/mir-opt/set_no_discriminant.rs diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index a41d8e212454..dcab124505e2 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -43,6 +43,7 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem}; +use rustc_target::abi::{TagEncoding, Variants}; use crate::cost_checker::CostChecker; @@ -391,8 +392,25 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { StatementKind::SetDiscriminant { box place, variant_index } => { let discr_target = self.map.find_discr(place.as_ref())?; let enum_ty = place.ty(self.body, self.tcx).ty; - let discr = discriminant_for_variant(enum_ty, *variant_index)?; - self.process_operand(bb, discr_target, &discr, state)?; + // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant + // of a niche encoding. If we cannot ensure that we write to the discriminant, do + // nothing. + let enum_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?; + let writes_discriminant = match enum_layout.variants { + Variants::Single { index } => { + assert_eq!(index, *variant_index); + true + } + Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => true, + Variants::Multiple { + tag_encoding: TagEncoding::Niche { untagged_variant, .. }, + .. + } => *variant_index != untagged_variant, + }; + if writes_discriminant { + let discr = discriminant_for_variant(enum_ty, *variant_index)?; + self.process_operand(bb, discr_target, &discr, state)?; + } } // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`. StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( diff --git a/tests/mir-opt/set_no_discriminant.f.JumpThreading.diff b/tests/mir-opt/set_no_discriminant.f.JumpThreading.diff new file mode 100644 index 000000000000..bc28e81c9a88 --- /dev/null +++ b/tests/mir-opt/set_no_discriminant.f.JumpThreading.diff @@ -0,0 +1,26 @@ +- // MIR for `f` before JumpThreading ++ // MIR for `f` after JumpThreading + + fn f() -> usize { + let mut _0: usize; + let mut _1: isize; + let mut _2: E; + + bb0: { + _2 = E::::A; + discriminant(_2) = 1; + _1 = discriminant(_2); + switchInt(_1) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + _0 = const 0_usize; + return; + } + + bb2: { + _0 = const 1_usize; + return; + } + } + diff --git a/tests/mir-opt/set_no_discriminant.generic.JumpThreading.diff b/tests/mir-opt/set_no_discriminant.generic.JumpThreading.diff new file mode 100644 index 000000000000..78bfeef3c649 --- /dev/null +++ b/tests/mir-opt/set_no_discriminant.generic.JumpThreading.diff @@ -0,0 +1,26 @@ +- // MIR for `generic` before JumpThreading ++ // MIR for `generic` after JumpThreading + + fn generic() -> usize { + let mut _0: usize; + let mut _1: isize; + let mut _2: E; + + bb0: { + _2 = E::::A; + discriminant(_2) = 1; + _1 = discriminant(_2); + switchInt(_1) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + _0 = const 0_usize; + return; + } + + bb2: { + _0 = const 1_usize; + return; + } + } + diff --git a/tests/mir-opt/set_no_discriminant.rs b/tests/mir-opt/set_no_discriminant.rs new file mode 100644 index 000000000000..8ffb9a2910ab --- /dev/null +++ b/tests/mir-opt/set_no_discriminant.rs @@ -0,0 +1,78 @@ +// `SetDiscriminant` does not actually write anything if the chosen variant is the untagged variant +// of a niche encoding. Verify that we do not thread over this case. +// unit-test: JumpThreading + +#![feature(custom_mir)] +#![feature(core_intrinsics)] + +use std::intrinsics::mir::*; + +enum E { + A, + B(T), +} + +// EMIT_MIR set_no_discriminant.f.JumpThreading.diff +#[custom_mir(dialect = "runtime")] +pub fn f() -> usize { + // CHECK-LABEL: fn f( + // CHECK-NOT: goto + // CHECK: switchInt( + // CHECK-NOT: goto + mir!( + let a: isize; + let e: E; + { + e = E::A; + SetDiscriminant(e, 1); + a = Discriminant(e); + match a { + 0 => bb0, + _ => bb1, + } + } + bb0 = { + RET = 0; + Return() + } + bb1 = { + RET = 1; + Return() + } + ) +} + +// EMIT_MIR set_no_discriminant.generic.JumpThreading.diff +#[custom_mir(dialect = "runtime")] +pub fn generic() -> usize { + // CHECK-LABEL: fn generic( + // CHECK-NOT: goto + // CHECK: switchInt( + // CHECK-NOT: goto + mir!( + let a: isize; + let e: E; + { + e = E::A; + SetDiscriminant(e, 1); + a = Discriminant(e); + match a { + 0 => bb0, + _ => bb1, + } + } + bb0 = { + RET = 0; + Return() + } + bb1 = { + RET = 1; + Return() + } + ) +} + +fn main() { + assert_eq!(f(), 0); + assert_eq!(generic::(), 0); +} From 373aeed24d86a6ece0a1d7a067fcc83a6c5bd41a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 7 Jan 2024 02:09:55 +0000 Subject: [PATCH 146/257] Ask for rustc version in diagnostic reports, remind users to update their toolchain --- .github/ISSUE_TEMPLATE/diagnostics.yaml | 19 ++++++++++++++++--- .github/ISSUE_TEMPLATE/ice.yaml | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/diagnostics.yaml b/.github/ISSUE_TEMPLATE/diagnostics.yaml index 873fbaaf654f..54bf2bc6ee24 100644 --- a/.github/ISSUE_TEMPLATE/diagnostics.yaml +++ b/.github/ISSUE_TEMPLATE/diagnostics.yaml @@ -52,10 +52,23 @@ body: render: Rust validations: required: false - - type: markdown + - type: textarea + id: version attributes: - value: | - If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. The output might also be different depending on the Edition. + label: Rust Version + description: Please provide the `rustc` version, `rustc --version --verbose`. Make sure that you're using the latest version of the compiler, and not an outdated stable or nightly release! + placeholder: | + $ rustc --version --verbose + rustc 1.XX.Y (SHORTHASH DATE) + binary: rustc + commit-hash: LONGHASHVALUE + commit-date: DATE + host: PLATFORMTRIPLE + release: 1.XX.Y + LLVM version: XX.YY.ZZ + render: Shell + validations: + required: true - type: textarea id: extra attributes: diff --git a/.github/ISSUE_TEMPLATE/ice.yaml b/.github/ISSUE_TEMPLATE/ice.yaml index 7bec05cc575a..13be13d4f998 100644 --- a/.github/ISSUE_TEMPLATE/ice.yaml +++ b/.github/ISSUE_TEMPLATE/ice.yaml @@ -40,7 +40,7 @@ body: id: version attributes: label: Rust Version - description: Please provide the `rustc` version, `rustc --version --verbose` + description: Please provide the `rustc` version, `rustc --version --verbose`. Make sure that you're using the latest version of the compiler, and not an outdated stable or nightly release! placeholder: | $ rustc --version --verbose rustc 1.XX.Y (SHORTHASH DATE) From fd8ba7bc3cfcc72f79da33f74b19c905f0cbb835 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sat, 29 Jul 2023 16:08:24 +0200 Subject: [PATCH 147/257] typo fix --- library/alloc/src/vec/into_iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index b03e04b7c706..3a5511fe08b7 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -136,7 +136,7 @@ impl IntoIter { /// Forgets to Drop the remaining elements while still allowing the backing allocation to be freed. pub(crate) fn forget_remaining_elements(&mut self) { - // For th ZST case, it is crucial that we mutate `end` here, not `ptr`. + // For the ZST case, it is crucial that we mutate `end` here, not `ptr`. // `ptr` must stay aligned, while `end` may be unaligned. self.end = self.ptr; } From 93b34a5ffa753d6cdaca8aab349408e945412ebd Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sat, 29 Jul 2023 16:25:53 +0200 Subject: [PATCH 148/257] mark vec::IntoIter pointers as `!nonnull` --- library/alloc/src/lib.rs | 2 + library/alloc/src/vec/in_place_collect.rs | 2 +- library/alloc/src/vec/into_iter.rs | 102 ++++++++++++++-------- library/alloc/src/vec/mod.rs | 10 +-- library/alloc/src/vec/spec_from_iter.rs | 4 +- tests/codegen/vec-iter.rs | 46 ++++++++++ 6 files changed, 119 insertions(+), 47 deletions(-) create mode 100644 tests/codegen/vec-iter.rs diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 493525534867..78629b39d34b 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -138,6 +138,7 @@ #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_uninit_array_transpose)] +#![feature(non_null_convenience)] #![feature(panic_internals)] #![feature(pattern)] #![feature(ptr_internals)] @@ -180,6 +181,7 @@ #![feature(const_ptr_write)] #![feature(const_trait_impl)] #![feature(const_try)] +#![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(exclusive_range_pattern)] #![feature(fundamental)] diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index a6cbed092c0a..0a2280545dac 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -257,7 +257,7 @@ where // then the source pointer will stay in its initial position and we can't use it as reference if src.ptr != src_ptr { debug_assert!( - unsafe { dst_buf.add(len) as *const _ } <= src.ptr, + unsafe { dst_buf.add(len) as *const _ } <= src.ptr.as_ptr(), "InPlaceIterable contract violation, write pointer advanced beyond read pointer" ); } diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 3a5511fe08b7..654ce09afcd3 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -18,6 +18,17 @@ use core::ops::Deref; use core::ptr::{self, NonNull}; use core::slice::{self}; +macro non_null { + (mut $place:expr, $t:ident) => {{ + #![allow(unused_unsafe)] // we're sometimes used within an unsafe block + unsafe { &mut *(ptr::addr_of_mut!($place) as *mut NonNull<$t>) } + }}, + ($place:expr, $t:ident) => {{ + #![allow(unused_unsafe)] // we're sometimes used within an unsafe block + unsafe { *(ptr::addr_of!($place) as *const NonNull<$t>) } + }}, +} + /// An iterator that moves out of a vector. /// /// This `struct` is created by the `into_iter` method on [`Vec`](super::Vec) @@ -41,10 +52,12 @@ pub struct IntoIter< // the drop impl reconstructs a RawVec from buf, cap and alloc // to avoid dropping the allocator twice we need to wrap it into ManuallyDrop pub(super) alloc: ManuallyDrop, - pub(super) ptr: *const T, - pub(super) end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that - // ptr == end is a quick test for the Iterator being empty, that works - // for both ZST and non-ZST. + pub(super) ptr: NonNull, + /// If T is a ZST, this is actually ptr+len. This encoding is picked so that + /// ptr == end is a quick test for the Iterator being empty, that works + /// for both ZST and non-ZST. + /// For non-ZSTs the pointer is treated as `NonNull` + pub(super) end: *const T, } #[stable(feature = "vec_intoiter_debug", since = "1.13.0")] @@ -68,7 +81,7 @@ impl IntoIter { /// ``` #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] pub fn as_slice(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.ptr, self.len()) } + unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len()) } } /// Returns the remaining items of this iterator as a mutable slice. @@ -97,7 +110,7 @@ impl IntoIter { } fn as_raw_mut_slice(&mut self) -> *mut [T] { - ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) + ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), self.len()) } /// Drops remaining elements and relinquishes the backing allocation. @@ -124,7 +137,7 @@ impl IntoIter { // this creates less assembly self.cap = 0; self.buf = unsafe { NonNull::new_unchecked(RawVec::NEW.ptr()) }; - self.ptr = self.buf.as_ptr(); + self.ptr = self.buf; self.end = self.buf.as_ptr(); // Dropping the remaining elements can panic, so this needs to be @@ -138,7 +151,7 @@ impl IntoIter { pub(crate) fn forget_remaining_elements(&mut self) { // For the ZST case, it is crucial that we mutate `end` here, not `ptr`. // `ptr` must stay aligned, while `end` may be unaligned. - self.end = self.ptr; + self.end = self.ptr.as_ptr(); } #[cfg(not(no_global_oom_handling))] @@ -160,7 +173,7 @@ impl IntoIter { // say that they're all at the beginning of the "allocation". 0..this.len() } else { - this.ptr.sub_ptr(buf)..this.end.sub_ptr(buf) + this.ptr.sub_ptr(this.buf)..this.end.sub_ptr(buf) }; let cap = this.cap; let alloc = ManuallyDrop::take(&mut this.alloc); @@ -187,29 +200,35 @@ impl Iterator for IntoIter { #[inline] fn next(&mut self) -> Option { - if self.ptr == self.end { - None - } else if T::IS_ZST { - // `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by - // reducing the `end`. - self.end = self.end.wrapping_byte_sub(1); + if T::IS_ZST { + if self.ptr.as_ptr() == self.end as *mut _ { + None + } else { + // `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by + // reducing the `end`. + self.end = self.end.wrapping_byte_sub(1); - // Make up a value of this ZST. - Some(unsafe { mem::zeroed() }) + // Make up a value of this ZST. + Some(unsafe { mem::zeroed() }) + } } else { - let old = self.ptr; - self.ptr = unsafe { self.ptr.add(1) }; + if self.ptr == non_null!(self.end, T) { + None + } else { + let old = self.ptr; + self.ptr = unsafe { old.add(1) }; - Some(unsafe { ptr::read(old) }) + Some(unsafe { ptr::read(old.as_ptr()) }) + } } } #[inline] fn size_hint(&self) -> (usize, Option) { let exact = if T::IS_ZST { - self.end.addr().wrapping_sub(self.ptr.addr()) + self.end.addr().wrapping_sub(self.ptr.as_ptr().addr()) } else { - unsafe { self.end.sub_ptr(self.ptr) } + unsafe { non_null!(self.end, T).sub_ptr(self.ptr) } }; (exact, Some(exact)) } @@ -217,7 +236,7 @@ impl Iterator for IntoIter { #[inline] fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize> { let step_size = self.len().min(n); - let to_drop = ptr::slice_from_raw_parts_mut(self.ptr as *mut T, step_size); + let to_drop = ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), step_size); if T::IS_ZST { // See `next` for why we sub `end` here. self.end = self.end.wrapping_byte_sub(step_size); @@ -259,7 +278,7 @@ impl Iterator for IntoIter { // Safety: `len` indicates that this many elements are available and we just checked that // it fits into the array. unsafe { - ptr::copy_nonoverlapping(self.ptr, raw_ary.as_mut_ptr() as *mut T, len); + ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, len); self.forget_remaining_elements(); return Err(array::IntoIter::new_unchecked(raw_ary, 0..len)); } @@ -268,7 +287,7 @@ impl Iterator for IntoIter { // Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize // the array. return unsafe { - ptr::copy_nonoverlapping(self.ptr, raw_ary.as_mut_ptr() as *mut T, N); + ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, N); self.ptr = self.ptr.add(N); Ok(raw_ary.transpose().assume_init()) }; @@ -286,7 +305,7 @@ impl Iterator for IntoIter { // Also note the implementation of `Self: TrustedRandomAccess` requires // that `T: Copy` so reading elements from the buffer doesn't invalidate // them for `Drop`. - unsafe { if T::IS_ZST { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } } + unsafe { if T::IS_ZST { mem::zeroed() } else { self.ptr.add(i).read() } } } } @@ -294,18 +313,25 @@ impl Iterator for IntoIter { impl DoubleEndedIterator for IntoIter { #[inline] fn next_back(&mut self) -> Option { - if self.end == self.ptr { - None - } else if T::IS_ZST { - // See above for why 'ptr.offset' isn't used - self.end = self.end.wrapping_byte_sub(1); + if T::IS_ZST { + if self.end as *mut _ == self.ptr.as_ptr() { + None + } else { + // See above for why 'ptr.offset' isn't used + self.end = self.end.wrapping_byte_sub(1); - // Make up a value of this ZST. - Some(unsafe { mem::zeroed() }) + // Make up a value of this ZST. + Some(unsafe { mem::zeroed() }) + } } else { - self.end = unsafe { self.end.sub(1) }; + if non_null!(self.end, T) == self.ptr { + None + } else { + let new_end = unsafe { non_null!(self.end, T).sub(1) }; + *non_null!(mut self.end, T) = new_end; - Some(unsafe { ptr::read(self.end) }) + Some(unsafe { ptr::read(new_end.as_ptr()) }) + } } } @@ -331,7 +357,11 @@ impl DoubleEndedIterator for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter { fn is_empty(&self) -> bool { - self.ptr == self.end + if T::IS_ZST { + self.ptr.as_ptr() == self.end as *mut _ + } else { + self.ptr == non_null!(self.end, T) + } } } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index e8a096cac869..8aa0c6e7ed62 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2825,14 +2825,8 @@ impl IntoIterator for Vec { begin.add(me.len()) as *const T }; let cap = me.buf.capacity(); - IntoIter { - buf: NonNull::new_unchecked(begin), - phantom: PhantomData, - cap, - alloc, - ptr: begin, - end, - } + let buf = NonNull::new_unchecked(begin); + IntoIter { buf, phantom: PhantomData, cap, alloc, ptr: buf, end } } } } diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs index efa6868473e4..e976552cf2b9 100644 --- a/library/alloc/src/vec/spec_from_iter.rs +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -44,12 +44,12 @@ impl SpecFromIter> for Vec { // than creating it through the generic FromIterator implementation would. That limitation // is not strictly necessary as Vec's allocation behavior is intentionally unspecified. // But it is a conservative choice. - let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr; + let has_advanced = iterator.buf != iterator.ptr; if !has_advanced || iterator.len() >= iterator.cap / 2 { unsafe { let it = ManuallyDrop::new(iterator); if has_advanced { - ptr::copy(it.ptr, it.buf.as_ptr(), it.len()); + ptr::copy(it.ptr.as_ptr(), it.buf.as_ptr(), it.len()); } return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap); } diff --git a/tests/codegen/vec-iter.rs b/tests/codegen/vec-iter.rs new file mode 100644 index 000000000000..0282791e9d16 --- /dev/null +++ b/tests/codegen/vec-iter.rs @@ -0,0 +1,46 @@ +// ignore-debug: the debug assertions get in the way +// compile-flags: -O +#![crate_type = "lib"] +#![feature(exact_size_is_empty)] + +use std::vec; + +// CHECK-LABEL: @vec_iter_len_nonnull +#[no_mangle] +pub fn vec_iter_len_nonnull(it: &vec::IntoIter) -> usize { + // CHECK: load ptr + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: load ptr + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: sub nuw + // CHECK: ret + it.len() +} + +// CHECK-LABEL: @vec_iter_is_empty_nonnull +#[no_mangle] +pub fn vec_iter_is_empty_nonnull(it: &vec::IntoIter) -> bool { + // CHECK: load ptr + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: load ptr + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: ret + it.is_empty() +} + +// CHECK-LABEL: @vec_iter_next +#[no_mangle] +pub fn vec_iter_next(it: &mut vec::IntoIter) -> Option { + // CHECK: load ptr + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: load ptr + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: ret + it.next() +} From 3c378b9c706c41d8f07adff9efd91ccb23e1c529 Mon Sep 17 00:00:00 2001 From: riverbl <94326797+riverbl@users.noreply.github.com> Date: Sat, 6 Jan 2024 22:03:45 +0000 Subject: [PATCH 149/257] Add inlay hint for exclusive ranges Adds an inlay hint containing a '<' character to exclusive range expressions and patterns that specify an upper bound. --- crates/hir-def/src/body/lower.rs | 2 +- .../src/handlers/convert_let_else_to_match.rs | 1 + crates/ide/src/inlay_hints.rs | 15 ++- crates/ide/src/inlay_hints/range_exclusive.rs | 121 ++++++++++++++++++ crates/ide/src/static_index.rs | 1 + .../rust-analyzer/src/cli/analysis_stats.rs | 1 + crates/rust-analyzer/src/config.rs | 3 + crates/syntax/src/ast.rs | 10 ++ crates/syntax/src/ast/expr_ext.rs | 24 ++-- crates/syntax/src/ast/node_ext.rs | 34 ++++- crates/syntax/src/ast/prec.rs | 2 +- crates/syntax/src/validation.rs | 2 +- docs/user/generated_config.adoc | 5 + editors/code/package.json | 5 + 14 files changed, 209 insertions(+), 17 deletions(-) create mode 100644 crates/ide/src/inlay_hints/range_exclusive.rs diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index bc4da360c5a2..6be3b141e679 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -17,7 +17,7 @@ use smallvec::SmallVec; use syntax::{ ast::{ self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName, - SlicePatComponents, + RangeItem, SlicePatComponents, }, AstNode, AstPtr, SyntaxNodePtr, }; diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 5f7056b9c1c4..79c34c14da72 100644 --- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -1,5 +1,6 @@ use hir::Semantics; use ide_db::RootDatabase; +use syntax::ast::RangeItem; use syntax::ast::{edit::AstNodeEdit, AstNode, HasName, LetStmt, Name, Pat}; use syntax::T; diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 6d8d10320510..f466b8e938f9 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -32,6 +32,7 @@ mod fn_lifetime_fn; mod implicit_static; mod param_name; mod implicit_drop; +mod range_exclusive; #[derive(Clone, Debug, PartialEq, Eq)] pub struct InlayHintsConfig { @@ -51,6 +52,7 @@ pub struct InlayHintsConfig { pub param_names_for_lifetime_elision_hints: bool, pub hide_named_constructor_hints: bool, pub hide_closure_initialization_hints: bool, + pub range_exclusive_hints: bool, pub closure_style: ClosureStyle, pub max_length: Option, pub closing_brace_hints_min_lines: Option, @@ -127,6 +129,7 @@ pub enum InlayKind { Parameter, Type, Drop, + RangeExclusive, } #[derive(Debug)] @@ -517,13 +520,20 @@ fn hints( closure_captures::hints(hints, famous_defs, config, file_id, it.clone()); closure_ret::hints(hints, famous_defs, config, file_id, it) }, + ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, config, it), _ => None, } }, ast::Pat(it) => { binding_mode::hints(hints, sema, config, &it); - if let ast::Pat::IdentPat(it) = it { - bind_pat::hints(hints, famous_defs, config, file_id, &it); + match it { + ast::Pat::IdentPat(it) => { + bind_pat::hints(hints, famous_defs, config, file_id, &it); + } + ast::Pat::RangePat(it) => { + range_exclusive::hints(hints, config, it); + } + _ => {} } Some(()) }, @@ -621,6 +631,7 @@ mod tests { closing_brace_hints_min_lines: None, fields_to_resolve: InlayFieldsToResolve::empty(), implicit_drop_hints: false, + range_exclusive_hints: false, }; pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig { type_hints: true, diff --git a/crates/ide/src/inlay_hints/range_exclusive.rs b/crates/ide/src/inlay_hints/range_exclusive.rs new file mode 100644 index 000000000000..50ab15c504f6 --- /dev/null +++ b/crates/ide/src/inlay_hints/range_exclusive.rs @@ -0,0 +1,121 @@ +//! Implementation of "range exclusive" inlay hints: +//! ```no_run +//! for i in 0../* < */10 {} +//! if let ../* < */100 = 50 {} +//! ``` +use syntax::{ast, SyntaxToken, T}; + +use crate::{InlayHint, InlayHintsConfig}; + +pub(super) fn hints( + acc: &mut Vec, + config: &InlayHintsConfig, + range: impl ast::RangeItem, +) -> Option<()> { + (config.range_exclusive_hints && range.end().is_some()) + .then(|| { + range.op_token().filter(|token| token.kind() == T![..]).map(|token| { + acc.push(inlay_hint(token)); + }) + }) + .flatten() +} + +fn inlay_hint(token: SyntaxToken) -> InlayHint { + InlayHint { + range: token.text_range(), + position: crate::InlayHintPosition::After, + pad_left: false, + pad_right: false, + kind: crate::InlayKind::RangeExclusive, + label: crate::InlayHintLabel::from("<"), + text_edit: None, + needs_resolve: false, + } +} + +#[cfg(test)] +mod tests { + use crate::{ + inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, + InlayHintsConfig, + }; + + #[test] + fn range_exclusive_expression_bounded_above_hints() { + check_with_config( + InlayHintsConfig { range_exclusive_hints: true, ..DISABLED_CONFIG }, + r#" +fn main() { + let a = 0..10; + //^^< + let b = ..100; + //^^< + let c = (2 - 1)..(7 * 8) + //^^< +}"#, + ); + } + + #[test] + fn range_exclusive_expression_unbounded_above_no_hints() { + check_with_config( + InlayHintsConfig { range_exclusive_hints: true, ..DISABLED_CONFIG }, + r#" +fn main() { + let a = 0..; + let b = ..; +}"#, + ); + } + + #[test] + fn range_inclusive_expression_no_hints() { + check_with_config( + InlayHintsConfig { range_exclusive_hints: true, ..DISABLED_CONFIG }, + r#" +fn main() { + let a = 0..=10; + let b = ..=100; +}"#, + ); + } + + #[test] + fn range_exclusive_pattern_bounded_above_hints() { + check_with_config( + InlayHintsConfig { range_exclusive_hints: true, ..DISABLED_CONFIG }, + r#" +fn main() { + if let 0..10 = 0 {} + //^^< + if let ..100 = 0 {} + //^^< +}"#, + ); + } + + #[test] + fn range_exclusive_pattern_unbounded_above_no_hints() { + check_with_config( + InlayHintsConfig { range_exclusive_hints: true, ..DISABLED_CONFIG }, + r#" +fn main() { + if let 0.. = 0 {} + if let .. = 0 {} +}"#, + ); + } + + #[test] + fn range_inclusive_pattern_no_hints() { + check_with_config( + InlayHintsConfig { range_exclusive_hints: true, ..DISABLED_CONFIG }, + r#" +fn main() { + if let 0..=10 = 0 {} + if let ..=100 = 0 {} +}"#, + ); + } +} diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 47fe2472a5e9..5b7094e6bcc0 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -133,6 +133,7 @@ impl StaticIndex<'_> { closure_capture_hints: false, closing_brace_hints_min_lines: Some(25), fields_to_resolve: InlayFieldsToResolve::empty(), + range_exclusive_hints: false, }, file_id, None, diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 1908c73b3b46..e69302dbaccf 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -792,6 +792,7 @@ impl flags::AnalysisStats { max_length: Some(25), closing_brace_hints_min_lines: Some(20), fields_to_resolve: InlayFieldsToResolve::empty(), + range_exclusive_hints: true, }, file_id, None, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 88fb3708449f..fe009f82a71f 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -399,6 +399,8 @@ config_data! { /// Whether to show function parameter name inlay hints at the call /// site. inlayHints_parameterHints_enable: bool = "true", + /// Whether to show exclusive range inlay hints. + inlayHints_rangeExclusiveHints_enable: bool = "false", /// Whether to show inlay hints for compiler inserted reborrows. /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. inlayHints_reborrowHints_enable: ReborrowHintsDef = "\"never\"", @@ -1464,6 +1466,7 @@ impl Config { } else { None }, + range_exclusive_hints: self.data.inlayHints_rangeExclusiveHints_enable, fields_to_resolve: InlayFieldsToResolve { resolve_text_edits: client_capability_fields.contains("textEdits"), resolve_hint_tooltip: client_capability_fields.contains("tooltip"), diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 1e691befff62..cc90d2dd1d55 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs @@ -136,6 +136,16 @@ where { } +/// Trait to describe operations common to both `RangeExpr` and `RangePat`. +pub trait RangeItem { + type Bound; + + fn start(&self) -> Option; + fn end(&self) -> Option; + fn op_kind(&self) -> Option; + fn op_token(&self) -> Option; +} + mod support { use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken}; diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index 36980b146ef5..18a56e2823ab 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs @@ -13,6 +13,8 @@ use crate::{ SyntaxNode, SyntaxToken, T, }; +use super::RangeItem; + impl ast::HasAttrs for ast::Expr {} impl ast::Expr { @@ -227,16 +229,12 @@ impl ast::RangeExpr { Some((ix, token, bin_op)) }) } +} - pub fn op_kind(&self) -> Option { - self.op_details().map(|t| t.2) - } +impl RangeItem for ast::RangeExpr { + type Bound = ast::Expr; - pub fn op_token(&self) -> Option { - self.op_details().map(|t| t.1) - } - - pub fn start(&self) -> Option { + fn start(&self) -> Option { let op_ix = self.op_details()?.0; self.syntax() .children_with_tokens() @@ -244,13 +242,21 @@ impl ast::RangeExpr { .find_map(|it| ast::Expr::cast(it.into_node()?)) } - pub fn end(&self) -> Option { + fn end(&self) -> Option { let op_ix = self.op_details()?.0; self.syntax() .children_with_tokens() .skip(op_ix + 1) .find_map(|it| ast::Expr::cast(it.into_node()?)) } + + fn op_token(&self) -> Option { + self.op_details().map(|t| t.1) + } + + fn op_kind(&self) -> Option { + self.op_details().map(|t| t.2) + } } impl ast::IndexExpr { diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index a7e4899fb7ee..8618018c0b6a 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -14,6 +14,8 @@ use crate::{ ted, NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T, }; +use super::{RangeItem, RangeOp}; + impl ast::Lifetime { pub fn text(&self) -> TokenText<'_> { text_of_first_token(self.syntax()) @@ -875,8 +877,10 @@ impl ast::Module { } } -impl ast::RangePat { - pub fn start(&self) -> Option { +impl RangeItem for ast::RangePat { + type Bound = ast::Pat; + + fn start(&self) -> Option { self.syntax() .children_with_tokens() .take_while(|it| !(it.kind() == T![..] || it.kind() == T![..=])) @@ -884,13 +888,37 @@ impl ast::RangePat { .find_map(ast::Pat::cast) } - pub fn end(&self) -> Option { + fn end(&self) -> Option { self.syntax() .children_with_tokens() .skip_while(|it| !(it.kind() == T![..] || it.kind() == T![..=])) .filter_map(|it| it.into_node()) .find_map(ast::Pat::cast) } + + fn op_token(&self) -> Option { + self.syntax().children_with_tokens().find_map(|it| { + let token = it.into_token()?; + + match token.kind() { + T![..] => Some(token), + T![..=] => Some(token), + _ => None, + } + }) + } + + fn op_kind(&self) -> Option { + self.syntax().children_with_tokens().find_map(|it| { + let token = it.into_token()?; + + match token.kind() { + T![..] => Some(RangeOp::Exclusive), + T![..=] => Some(RangeOp::Inclusive), + _ => None, + } + }) + } } impl ast::TokenTree { diff --git a/crates/syntax/src/ast/prec.rs b/crates/syntax/src/ast/prec.rs index 8e04ab8bedcb..9ddf5a0a9804 100644 --- a/crates/syntax/src/ast/prec.rs +++ b/crates/syntax/src/ast/prec.rs @@ -1,7 +1,7 @@ //! Precedence representation. use crate::{ - ast::{self, BinaryOp, Expr, HasArgList}, + ast::{self, BinaryOp, Expr, HasArgList, RangeItem}, match_ast, AstNode, SyntaxNode, }; diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 2b1bbac08e52..eabbda2c3983 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs @@ -9,7 +9,7 @@ use rustc_dependencies::lexer::unescape::{self, unescape_literal, Mode}; use crate::{ algo, - ast::{self, HasAttrs, HasVisibility, IsString}, + ast::{self, HasAttrs, HasVisibility, IsString, RangeItem}, match_ast, AstNode, SyntaxError, SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS}, SyntaxNode, SyntaxToken, TextSize, T, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index c3f249e0ce2c..ecc90abff135 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -596,6 +596,11 @@ Maximum length for inlay hints. Set to null to have an unlimited length. Whether to show function parameter name inlay hints at the call site. -- +[[rust-analyzer.inlayHints.rangeExclusiveHints.enable]]rust-analyzer.inlayHints.rangeExclusiveHints.enable (default: `false`):: ++ +-- +Whether to show exclusive range inlay hints. +-- [[rust-analyzer.inlayHints.reborrowHints.enable]]rust-analyzer.inlayHints.reborrowHints.enable (default: `"never"`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 27ed8ac502b5..8307f6833e6f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1308,6 +1308,11 @@ "default": true, "type": "boolean" }, + "rust-analyzer.inlayHints.rangeExclusiveHints.enable": { + "markdownDescription": "Whether to show exclusive range inlay hints.", + "default": false, + "type": "boolean" + }, "rust-analyzer.inlayHints.reborrowHints.enable": { "markdownDescription": "Whether to show inlay hints for compiler inserted reborrows.\nThis setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.", "default": "never", From 6a409ddbe40f55dd5f89442d24548b42b0822f60 Mon Sep 17 00:00:00 2001 From: jyn Date: Mon, 25 Dec 2023 07:46:12 -0500 Subject: [PATCH 150/257] add a new `optimized_compiler_builtins` option in particular, this makes the `c` feature for compiler-builtins an explicit opt-in, rather than silently detected by whether `llvm-project` is checked out on disk. exposing this is necessary because the `cc` crate doesn't support cross-compiling to MSVC, and we want people to be able to run `x check --target foo` regardless of whether they have a c toolchain available. this also uses the new option in CI, where we *do* want to optimize compiler_builtins. the new option is off by default for the `dev` channel and on otherwise. --- config.example.toml | 8 +++++++ src/bootstrap/src/core/build_steps/compile.rs | 21 ++++++++++++++----- src/bootstrap/src/core/config/config.rs | 6 ++++++ src/bootstrap/src/utils/change_tracker.rs | 5 +++++ .../disabled/dist-x86_64-haiku/Dockerfile | 2 ++ .../host-x86_64/x86_64-gnu-llvm-17/Dockerfile | 1 + src/ci/run.sh | 9 ++++++++ 7 files changed, 47 insertions(+), 5 deletions(-) diff --git a/config.example.toml b/config.example.toml index f1ea6bac3ca1..a5ef4022d39d 100644 --- a/config.example.toml +++ b/config.example.toml @@ -339,6 +339,14 @@ # on this runtime, such as `-C profile-generate` or `-C instrument-coverage`). #profiler = false +# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics. +# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt` +# sources are available. +# +# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in +# order to run `x check`. +#optimized-compiler-builtins = if rust.channel == "dev" { false } else { true } + # Indicates whether the native libraries linked into Cargo will be statically # linked or not. #cargo-native-static = false diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index dbb64583d561..1769e94473ae 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -382,9 +382,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // Determine if we're going to compile in optimized C intrinsics to // the `compiler-builtins` crate. These intrinsics live in LLVM's - // `compiler-rt` repository, but our `src/llvm-project` submodule isn't - // always checked out, so we need to conditionally look for this. (e.g. if - // an external LLVM is used we skip the LLVM submodule checkout). + // `compiler-rt` repository. // // Note that this shouldn't affect the correctness of `compiler-builtins`, // but only its speed. Some intrinsics in C haven't been translated to Rust @@ -395,8 +393,21 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // If `compiler-rt` is available ensure that the `c` feature of the // `compiler-builtins` crate is enabled and it's configured to learn where // `compiler-rt` is located. - let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); - let compiler_builtins_c_feature = if compiler_builtins_root.exists() { + let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins { + // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op. + // But, the user could still decide to manually use an in-tree submodule. + // + // NOTE: if we're using system llvm, we'll end up building a version of `compiler-rt` that doesn't match the LLVM we're linking to. + // That's probably ok? At least, the difference wasn't enforced before. There's a comment in + // the compiler_builtins build script that makes me nervous, though: + // https://github.com/rust-lang/compiler-builtins/blob/31ee4544dbe47903ce771270d6e3bea8654e9e50/build.rs#L575-L579 + builder.update_submodule(&Path::new("src").join("llvm-project")); + let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); + if !compiler_builtins_root.exists() { + panic!( + "need LLVM sources available to build `compiler-rt`, but they weren't present; consider enabling `build.submodules = true` or disabling `optimized-compiler-builtins`" + ); + } // Note that `libprofiler_builtins/build.rs` also computes this so if // you're changing something here please also change that. cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index a997eccb39ad..8716ba539032 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -178,6 +178,8 @@ pub struct Config { pub patch_binaries_for_nix: Option, pub stage0_metadata: Stage0Metadata, pub android_ndk: Option, + /// Whether to use the `c` feature of the `compiler_builtins` crate. + pub optimized_compiler_builtins: bool, pub stdout_is_tty: bool, pub stderr_is_tty: bool, @@ -848,6 +850,7 @@ define_config! { // NOTE: only parsed by bootstrap.py, `--feature build-metrics` enables metrics unconditionally metrics: Option = "metrics", android_ndk: Option = "android-ndk", + optimized_compiler_builtins: Option = "optimized-compiler-builtins", } } @@ -1396,6 +1399,7 @@ impl Config { // This field is only used by bootstrap.py metrics: _, android_ndk, + optimized_compiler_builtins, } = toml.build.unwrap_or_default(); if let Some(file_build) = build { @@ -1916,6 +1920,8 @@ impl Config { config.rust_debuginfo_level_std = with_defaults(debuginfo_level_std); config.rust_debuginfo_level_tools = with_defaults(debuginfo_level_tools); config.rust_debuginfo_level_tests = debuginfo_level_tests.unwrap_or(DebuginfoLevel::None); + config.optimized_compiler_builtins = + optimized_compiler_builtins.unwrap_or(config.channel != "dev"); let download_rustc = config.download_rustc_commit.is_some(); // See https://github.com/rust-lang/compiler-team/issues/326 diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 25efa5079c87..327b4674acfd 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -106,4 +106,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "The dist.missing-tools config option was deprecated, as it was unused. If you are using it, remove it from your config, it will be removed soon.", }, + ChangeInfo { + change_id: 102579, + severity: ChangeSeverity::Warning, + summary: "A new `optimized-compiler-builtins` option has been introduced. Whether to build llvm's `compiler-rt` from source is no longer implicitly controlled by git state. See the PR for more details.", + }, ]; diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile index 5ddd3f180396..637b5fa22f97 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile @@ -47,4 +47,6 @@ ENV RUST_CONFIGURE_ARGS --disable-jemalloc \ --set=$TARGET.cc=x86_64-unknown-haiku-gcc \ --set=$TARGET.cxx=x86_64-unknown-haiku-g++ \ --set=$TARGET.llvm-config=/bin/llvm-config-haiku +ENV EXTERNAL_LLVM 1 + ENV SCRIPT python3 ../x.py dist --host=$HOST --target=$HOST diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile index f1d6b9a4ef26..fe30a9534410 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile @@ -40,6 +40,7 @@ RUN sh /scripts/sccache.sh # We are disabling CI LLVM since this builder is intentionally using a host # LLVM, rather than the typical src/llvm-project LLVM. ENV NO_DOWNLOAD_CI_LLVM 1 +ENV EXTERNAL_LLVM 1 # Using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS \ diff --git a/src/ci/run.sh b/src/ci/run.sh index e48fac1a0879..e9c274ca4e33 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -80,6 +80,15 @@ fi # space required for CI artifacts. RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --dist-compression-formats=xz" +# Enable the `c` feature for compiler_builtins, but only when the `compiler-rt` source is available +# (to avoid spending a lot of time cloning llvm) +if [ "$EXTERNAL_LLVM" = "" ]; then + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.optimized-compiler-builtins" +elif [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then + echo "error: dist builds should always use optimized compiler-rt!" >&2 + exit 1 +fi + if [ "$DIST_SRC" = "" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-dist-src" fi From 957a46fa69db5a67fad317305213b51d27e9df6e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 16 Nov 2023 12:32:51 +1100 Subject: [PATCH 151/257] coverage: Anonymize line numbers in branch views The code for anonymizing line numbers in coverage reports now supports the slightly different line number syntax used by branch regions. --- src/tools/compiletest/src/runtest.rs | 24 +++++++- src/tools/compiletest/src/runtest/tests.rs | 72 ++++++++++++++++++++++ 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index b258b748ca87..8be4def15ded 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -19,7 +19,6 @@ use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile}; use regex::{Captures, Regex}; use rustfix::{apply_suggestions, get_suggestions_from_json, Filter}; -use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::env; use std::ffi::{OsStr, OsString}; @@ -725,7 +724,7 @@ impl<'test> TestCx<'test> { /// Replace line numbers in coverage reports with the placeholder `LL`, /// so that the tests are less sensitive to lines being added/removed. - fn anonymize_coverage_line_numbers(coverage: &str) -> Cow<'_, str> { + fn anonymize_coverage_line_numbers(coverage: &str) -> String { // The coverage reporter prints line numbers at the start of a line. // They are truncated or left-padded to occupy exactly 5 columns. // (`LineNumberColumnWidth` in `SourceCoverageViewText.cpp`.) @@ -733,9 +732,28 @@ impl<'test> TestCx<'test> { // // Line numbers that appear inside expansion/instantiation subviews // have an additional prefix of ` |` for each nesting level. + // + // Branch views also include the relevant line number, so we want to + // redact those too. (These line numbers don't have padding.) + // + // Note: The pattern `(?m:^)` matches the start of a line. + + // ` 1|` => ` LL|` + // ` 10|` => ` LL|` + // ` 100|` => ` LL|` + // ` | 1000|` => ` | LL|` + // ` | | 1000|` => ` | | LL|` static LINE_NUMBER_RE: Lazy = Lazy::new(|| Regex::new(r"(?m:^)(?(?: \|)*) *[0-9]+\|").unwrap()); - LINE_NUMBER_RE.replace_all(coverage, "$prefix LL|") + let coverage = LINE_NUMBER_RE.replace_all(&coverage, "${prefix} LL|"); + + // ` | Branch (1:` => ` | Branch (LL:` + // ` | | Branch (10:` => ` | | Branch (LL:` + static BRANCH_LINE_NUMBER_RE: Lazy = + Lazy::new(|| Regex::new(r"(?m:^)(?(?: \|)+ Branch \()[0-9]+:").unwrap()); + let coverage = BRANCH_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:"); + + coverage.into_owned() } /// Coverage reports can describe multiple source files, separated by diff --git a/src/tools/compiletest/src/runtest/tests.rs b/src/tools/compiletest/src/runtest/tests.rs index fb3dd326a4c8..ee42243e83d8 100644 --- a/src/tools/compiletest/src/runtest/tests.rs +++ b/src/tools/compiletest/src/runtest/tests.rs @@ -48,3 +48,75 @@ fn normalize_platform_differences() { r#"println!("test\ntest")"#, ); } + +/// Test for anonymizing line numbers in coverage reports, especially for +/// branch regions. +/// +/// FIXME(#119681): This test can be removed when we have examples of branch +/// coverage in the actual coverage test suite. +#[test] +fn anonymize_coverage_line_numbers() { + let anon = |coverage| TestCx::anonymize_coverage_line_numbers(coverage); + + let input = r#" + 6| 3|fn print_size() { + 7| 3| if std::mem::size_of::() > 4 { + ------------------ + | Branch (7:8): [True: 0, False: 1] + | Branch (7:8): [True: 0, False: 1] + | Branch (7:8): [True: 1, False: 0] + ------------------ + 8| 1| println!("size > 4"); +"#; + + let expected = r#" + LL| 3|fn print_size() { + LL| 3| if std::mem::size_of::() > 4 { + ------------------ + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:8): [True: 1, False: 0] + ------------------ + LL| 1| println!("size > 4"); +"#; + + assert_eq!(anon(input), expected); + + ////////// + + let input = r#" + 12| 3|} + ------------------ + | branch_generics::print_size::<()>: + | 6| 1|fn print_size() { + | 7| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (7:8): [True: 0, False: 1] + | ------------------ + | 8| 0| println!("size > 4"); + | 9| 1| } else { + | 10| 1| println!("size <= 4"); + | 11| 1| } + | 12| 1|} + ------------------ +"#; + + let expected = r#" + LL| 3|} + ------------------ + | branch_generics::print_size::<()>: + | LL| 1|fn print_size() { + | LL| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (LL:8): [True: 0, False: 1] + | ------------------ + | LL| 0| println!("size > 4"); + | LL| 1| } else { + | LL| 1| println!("size <= 4"); + | LL| 1| } + | LL| 1|} + ------------------ +"#; + + assert_eq!(anon(input), expected); +} From 747fa7dd6ec269ce25a44f67a13450b393a15f9e Mon Sep 17 00:00:00 2001 From: homersimpsons Date: Sun, 7 Jan 2024 14:25:42 +0100 Subject: [PATCH 152/257] line-index: Create README.md --- lib/line-index/README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 lib/line-index/README.md diff --git a/lib/line-index/README.md b/lib/line-index/README.md new file mode 100644 index 000000000000..c0d310c857cd --- /dev/null +++ b/lib/line-index/README.md @@ -0,0 +1,28 @@ +# line-index + +This crate is developped as part of `rust-analyzer`. + +line-index is a library to convert between text offset and its corresponding line/column. + +## Installation + +To add this crate to a project simply run `cargo add line-index`. + +## Usage + +The main structure is `LineIndex`. It is constructed with an utf-8 text then various utility functions can be used on it. + +### Example + +```rust +use line_index::LineIndex; + +let line_index = LineIndex::new("This is a\nmulti-line\ntext."); +line_index.line_col(3.into()); // LineCol { line: 0, col: 3 } +line_index.line_col(13.into()); // LineCol { line: 1, col: 3 } +line_index.offset(LineCol { line: 2, col: 3 }); // Some (24) +``` + +## SemVer + +This crate follows [semver principles]([url](https://semver.org/)https://semver.org/). From a8aa6878f63e53a9b0cfee542e9765407e1ca0d6 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 22 Dec 2023 15:12:01 +0300 Subject: [PATCH 153/257] Update test for `E0796` and `static_mut_ref` lint --- .../example/mini_core_hello_world.rs | 3 + .../example/mini_core_hello_world.rs | 3 + library/panic_unwind/src/seh.rs | 4 + library/std/src/panicking.rs | 2 + .../src/sys/common/thread_local/fast_local.rs | 2 + .../sys/common/thread_local/static_local.rs | 2 + library/std/src/thread/local.rs | 2 + src/tools/lint-docs/src/groups.rs | 1 + .../miri/tests/fail/tls/tls_static_dealloc.rs | 2 + src/tools/miri/tests/pass/static_mut.rs | 3 + src/tools/miri/tests/pass/tls/tls_static.rs | 2 + tests/ui/abi/statics/static-mut-foreign.rs | 2 + .../ui/abi/statics/static-mut-foreign.stderr | 31 +++++++ .../borrowck/borrowck-access-permissions.rs | 36 +++++--- .../borrowck-access-permissions.stderr | 33 +++++-- .../borrowck-unsafe-static-mutable-borrows.rs | 9 +- ...rowck-unsafe-static-mutable-borrows.stderr | 17 ++++ tests/ui/borrowck/issue-20801.rs | 1 + tests/ui/borrowck/issue-20801.stderr | 25 ++++-- ...ue-55492-borrowck-migrate-scans-parents.rs | 41 ++++++--- ...5492-borrowck-migrate-scans-parents.stderr | 75 +++++++++++++--- tests/ui/consts/const_let_assign2.rs | 1 + tests/ui/consts/const_let_assign2.stderr | 17 ++++ .../ui/consts/issue-17718-const-bad-values.rs | 3 +- .../issue-17718-const-bad-values.stderr | 17 +++- ..._refers_to_static_cross_crate.32bit.stderr | 77 +++++++++------- ..._refers_to_static_cross_crate.64bit.stderr | 77 +++++++++------- .../const_refers_to_static_cross_crate.rs | 22 +++-- .../consts/static_mut_containing_mut_ref.rs | 1 + .../static_mut_containing_mut_ref.stderr | 17 ++++ ...ic_mut_containing_mut_ref2.mut_refs.stderr | 25 ++++-- .../consts/static_mut_containing_mut_ref2.rs | 10 ++- ...tatic_mut_containing_mut_ref2.stock.stderr | 25 ++++-- .../issue-23338-ensure-param-drop-order.rs | 90 ++++++++++--------- ...issue-23338-ensure-param-drop-order.stderr | 17 ++++ tests/ui/error-codes/E0017.rs | 12 ++- tests/ui/error-codes/E0017.stderr | 29 ++++-- .../bare_type.rs} | 9 +- .../issues/issue-23611-enum-swap-in-drop.rs | 44 ++++----- .../issue-23611-enum-swap-in-drop.stderr | 17 ++++ ...ead-local-static-mut-borrow-outlives-fn.rs | 5 +- ...local-static-mut-borrow-outlives-fn.stderr | 17 ++++ .../reference-of-mut-static-safe.e2021.stderr | 4 +- tests/ui/static/safe-extern-statics-mut.rs | 2 + .../ui/static/safe-extern-statics-mut.stderr | 35 +++++++- tests/ui/statics/issue-15261.rs | 3 +- tests/ui/statics/issue-15261.stderr | 17 ++++ tests/ui/statics/static-mut-xc.rs | 3 +- tests/ui/statics/static-mut-xc.stderr | 31 +++++++ tests/ui/statics/static-recursive.rs | 27 +++--- tests/ui/statics/static-recursive.stderr | 17 ++++ tests/ui/thread-local/thread-local-static.rs | 3 +- .../thread-local/thread-local-static.stderr | 17 +++- .../thread-local-static.thir.stderr | 59 ++++++++++++ 54 files changed, 807 insertions(+), 239 deletions(-) create mode 100644 tests/ui/abi/statics/static-mut-foreign.stderr create mode 100644 tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr create mode 100644 tests/ui/consts/const_let_assign2.stderr create mode 100644 tests/ui/consts/static_mut_containing_mut_ref.stderr create mode 100644 tests/ui/drop/issue-23338-ensure-param-drop-order.stderr rename tests/ui/{issues/issue-20616.rs => impl-header-lifetime-elision/bare_type.rs} (82%) create mode 100644 tests/ui/issues/issue-23611-enum-swap-in-drop.stderr create mode 100644 tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr create mode 100644 tests/ui/statics/issue-15261.stderr create mode 100644 tests/ui/statics/static-mut-xc.stderr create mode 100644 tests/ui/statics/static-recursive.stderr create mode 100644 tests/ui/thread-local/thread-local-static.thir.stderr diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index a1cdf31c68a0..2a7b1107ffca 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -111,6 +111,9 @@ fn start( } static mut NUM: u8 = 6 * 7; + +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint +#[allow(static_mut_ref)] static NUM_REF: &'static u8 = unsafe { &NUM }; unsafe fn zeroed() -> T { diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index 40a1ad22c0e1..9827e299f2a3 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -98,6 +98,9 @@ fn start( } static mut NUM: u8 = 6 * 7; + +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint +#[allow(static_mut_ref)] static NUM_REF: &'static u8 = unsafe { &NUM }; macro_rules! assert { diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 99db00e54906..ccae406cc286 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -261,6 +261,8 @@ cfg_if::cfg_if! { } } +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint +#[cfg_attr(not(bootstrap), allow(static_mut_ref))] pub unsafe fn panic(data: Box) -> u32 { use core::intrinsics::atomic_store_seqcst; @@ -322,6 +324,8 @@ pub unsafe fn panic(data: Box) -> u32 { _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _); } +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint +#[cfg_attr(not(bootstrap), allow(static_mut_ref))] pub unsafe fn cleanup(payload: *mut u8) -> Box { // A null payload here means that we got here from the catch (...) of // __rust_try. This happens when a non-Rust foreign exception is caught. diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 66b4ec37c8ec..c80f15d8a611 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -337,6 +337,8 @@ pub mod panic_count { #[doc(hidden)] #[cfg(not(feature = "panic_immediate_abort"))] #[unstable(feature = "update_panic_count", issue = "none")] +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint +#[cfg_attr(not(bootstrap), allow(static_mut_ref))] pub mod panic_count { use crate::cell::Cell; use crate::sync::atomic::{AtomicUsize, Ordering}; diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs index c0a9619bf7bf..9206588be064 100644 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -13,6 +13,8 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ #[inline] #[deny(unsafe_op_in_unsafe_fn)] + // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint + #[cfg_attr(not(bootstrap), allow(static_mut_ref))] unsafe fn __getit( _init: $crate::option::Option<&mut $crate::option::Option<$t>>, ) -> $crate::option::Option<&'static $t> { diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs index 5cb6c541a0ec..51cba66fad76 100644 --- a/library/std/src/sys/common/thread_local/static_local.rs +++ b/library/std/src/sys/common/thread_local/static_local.rs @@ -11,6 +11,8 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ #[inline] // see comments below #[deny(unsafe_op_in_unsafe_fn)] + // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint + #[cfg_attr(not(bootstrap), allow(static_mut_ref))] unsafe fn __getit( _init: $crate::option::Option<&mut $crate::option::Option<$t>>, ) -> $crate::option::Option<&'static $t> { diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 9cf37b0e6347..338567777f75 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -180,6 +180,8 @@ impl fmt::Debug for LocalKey { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")] #[allow_internal_unstable(thread_local_internals)] +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint +#[cfg_attr(not(bootstrap), allow(static_mut_ref))] macro_rules! thread_local { // empty (base case for the recursion) () => {}; diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs index 5be8ef7996bb..c5cd30ccc343 100644 --- a/src/tools/lint-docs/src/groups.rs +++ b/src/tools/lint-docs/src/groups.rs @@ -15,6 +15,7 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[ ("future-incompatible", "Lints that detect code that has future-compatibility problems"), ("rust-2018-compatibility", "Lints used to transition code from the 2015 edition to 2018"), ("rust-2021-compatibility", "Lints used to transition code from the 2018 edition to 2021"), + ("rust-2024-compatibility", "Lints used to transition code from the 2021 edition to 2024"), ]; type LintGroups = BTreeMap>; diff --git a/src/tools/miri/tests/fail/tls/tls_static_dealloc.rs b/src/tools/miri/tests/fail/tls/tls_static_dealloc.rs index d5e6d37226ab..762a8d85314f 100644 --- a/src/tools/miri/tests/fail/tls/tls_static_dealloc.rs +++ b/src/tools/miri/tests/fail/tls/tls_static_dealloc.rs @@ -1,6 +1,8 @@ //! Ensure that thread-local statics get deallocated when the thread dies. #![feature(thread_local)] +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint +#![allow(static_mut_ref)] #[thread_local] static mut TLS: u8 = 0; diff --git a/src/tools/miri/tests/pass/static_mut.rs b/src/tools/miri/tests/pass/static_mut.rs index 218b02525bd5..c1e58b70adb0 100644 --- a/src/tools/miri/tests/pass/static_mut.rs +++ b/src/tools/miri/tests/pass/static_mut.rs @@ -1,4 +1,7 @@ static mut FOO: i32 = 42; + +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint +#[allow(static_mut_ref)] static BAR: Foo = Foo(unsafe { &FOO as *const _ }); #[allow(dead_code)] diff --git a/src/tools/miri/tests/pass/tls/tls_static.rs b/src/tools/miri/tests/pass/tls/tls_static.rs index fc4c8a283ddb..9be00af47aa3 100644 --- a/src/tools/miri/tests/pass/tls/tls_static.rs +++ b/src/tools/miri/tests/pass/tls/tls_static.rs @@ -8,6 +8,8 @@ //! test, we also check that thread-locals act as per-thread statics. #![feature(thread_local)] +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint +#![allow(static_mut_ref)] use std::thread; diff --git a/tests/ui/abi/statics/static-mut-foreign.rs b/tests/ui/abi/statics/static-mut-foreign.rs index ecd8ee94a01e..eb732e7c2c31 100644 --- a/tests/ui/abi/statics/static-mut-foreign.rs +++ b/tests/ui/abi/statics/static-mut-foreign.rs @@ -33,7 +33,9 @@ unsafe fn run() { rust_dbg_static_mut = -3; assert_eq!(rust_dbg_static_mut, -3); static_bound(&rust_dbg_static_mut); + //~^ WARN shared reference of mutable static is discouraged [static_mut_ref] static_bound_set(&mut rust_dbg_static_mut); + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] } pub fn main() { diff --git a/tests/ui/abi/statics/static-mut-foreign.stderr b/tests/ui/abi/statics/static-mut-foreign.stderr new file mode 100644 index 000000000000..144ac056f87e --- /dev/null +++ b/tests/ui/abi/statics/static-mut-foreign.stderr @@ -0,0 +1,31 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/static-mut-foreign.rs:35:18 + | +LL | static_bound(&rust_dbg_static_mut); + | ^^^^^^^^^^^^^^^^^^^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | static_bound(addr_of!(rust_dbg_static_mut)); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +warning: mutable reference of mutable static is discouraged + --> $DIR/static-mut-foreign.rs:37:22 + | +LL | static_bound_set(&mut rust_dbg_static_mut); + | ^^^^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | static_bound_set(addr_of_mut!(rust_dbg_static_mut)); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +warning: 2 warnings emitted + diff --git a/tests/ui/borrowck/borrowck-access-permissions.rs b/tests/ui/borrowck/borrowck-access-permissions.rs index 469ad508b0e7..1638644103ba 100644 --- a/tests/ui/borrowck/borrowck-access-permissions.rs +++ b/tests/ui/borrowck/borrowck-access-permissions.rs @@ -1,21 +1,27 @@ -static static_x : i32 = 1; -static mut static_x_mut : i32 = 1; +static static_x: i32 = 1; +static mut static_x_mut: i32 = 1; fn main() { let x = 1; let mut x_mut = 1; - { // borrow of local + { + // borrow of local let _y1 = &mut x; //~ ERROR [E0596] let _y2 = &mut x_mut; // No error } - { // borrow of static + { + // borrow of static let _y1 = &mut static_x; //~ ERROR [E0596] - unsafe { let _y2 = &mut static_x_mut; } // No error + unsafe { + let _y2 = &mut static_x_mut; + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] + } } - { // borrow of deref to box + { + // borrow of deref to box let box_x = Box::new(1); let mut box_x_mut = Box::new(1); @@ -23,7 +29,8 @@ fn main() { let _y2 = &mut *box_x_mut; // No error } - { // borrow of deref to reference + { + // borrow of deref to reference let ref_x = &x; let ref_x_mut = &mut x_mut; @@ -31,9 +38,10 @@ fn main() { let _y2 = &mut *ref_x_mut; // No error } - { // borrow of deref to pointer - let ptr_x : *const _ = &x; - let ptr_mut_x : *mut _ = &mut x_mut; + { + // borrow of deref to pointer + let ptr_x: *const _ = &x; + let ptr_mut_x: *mut _ = &mut x_mut; unsafe { let _y1 = &mut *ptr_x; //~ ERROR [E0596] @@ -41,8 +49,12 @@ fn main() { } } - { // borrowing mutably through an immutable reference - struct Foo<'a> { f: &'a mut i32, g: &'a i32 }; + { + // borrowing mutably through an immutable reference + struct Foo<'a> { + f: &'a mut i32, + g: &'a i32, + }; let mut foo = Foo { f: &mut x_mut, g: &x }; let foo_ref = &foo; let _y = &mut *foo_ref.f; //~ ERROR [E0596] diff --git a/tests/ui/borrowck/borrowck-access-permissions.stderr b/tests/ui/borrowck/borrowck-access-permissions.stderr index c161e2d95b43..93d92295dd9b 100644 --- a/tests/ui/borrowck/borrowck-access-permissions.stderr +++ b/tests/ui/borrowck/borrowck-access-permissions.stderr @@ -1,5 +1,20 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/borrowck-access-permissions.rs:18:23 + | +LL | let _y2 = &mut static_x_mut; + | ^^^^^^^^^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | let _y2 = addr_of_mut!(static_x_mut); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable - --> $DIR/borrowck-access-permissions.rs:9:19 + --> $DIR/borrowck-access-permissions.rs:10:19 | LL | let _y1 = &mut x; | ^^^^^^ cannot borrow as mutable @@ -10,13 +25,13 @@ LL | let mut x = 1; | +++ error[E0596]: cannot borrow immutable static item `static_x` as mutable - --> $DIR/borrowck-access-permissions.rs:14:19 + --> $DIR/borrowck-access-permissions.rs:16:19 | LL | let _y1 = &mut static_x; | ^^^^^^^^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow `*box_x` as mutable, as `box_x` is not declared as mutable - --> $DIR/borrowck-access-permissions.rs:22:19 + --> $DIR/borrowck-access-permissions.rs:28:19 | LL | let _y1 = &mut *box_x; | ^^^^^^^^^^^ cannot borrow as mutable @@ -27,7 +42,7 @@ LL | let mut box_x = Box::new(1); | +++ error[E0596]: cannot borrow `*ref_x` as mutable, as it is behind a `&` reference - --> $DIR/borrowck-access-permissions.rs:30:19 + --> $DIR/borrowck-access-permissions.rs:37:19 | LL | let _y1 = &mut *ref_x; | ^^^^^^^^^^^ `ref_x` is a `&` reference, so the data it refers to cannot be borrowed as mutable @@ -38,18 +53,18 @@ LL | let ref_x = &mut x; | +++ error[E0596]: cannot borrow `*ptr_x` as mutable, as it is behind a `*const` pointer - --> $DIR/borrowck-access-permissions.rs:39:23 + --> $DIR/borrowck-access-permissions.rs:47:23 | LL | let _y1 = &mut *ptr_x; | ^^^^^^^^^^^ `ptr_x` is a `*const` pointer, so the data it refers to cannot be borrowed as mutable | help: consider changing this to be a mutable pointer | -LL | let ptr_x : *const _ = &mut x; - | +++ +LL | let ptr_x: *const _ = &mut x; + | +++ error[E0596]: cannot borrow `*foo_ref.f` as mutable, as it is behind a `&` reference - --> $DIR/borrowck-access-permissions.rs:48:18 + --> $DIR/borrowck-access-permissions.rs:60:18 | LL | let _y = &mut *foo_ref.f; | ^^^^^^^^^^^^^^^ `foo_ref` is a `&` reference, so the data it refers to cannot be borrowed as mutable @@ -59,6 +74,6 @@ help: consider changing this to be a mutable reference LL | let foo_ref = &mut foo; | +++ -error: aborting due to 6 previous errors +error: aborting due to 6 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs index adc7dfd541f4..1bf079e24cae 100644 --- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs @@ -2,17 +2,22 @@ // Test file taken from issue 45129 (https://github.com/rust-lang/rust/issues/45129) -struct Foo { x: [usize; 2] } +struct Foo { + x: [usize; 2], +} static mut SFOO: Foo = Foo { x: [23, 32] }; impl Foo { - fn x(&mut self) -> &mut usize { &mut self.x[0] } + fn x(&mut self) -> &mut usize { + &mut self.x[0] + } } fn main() { unsafe { let sfoo: *mut Foo = &mut SFOO; + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] let x = (*sfoo).x(); (*sfoo).x[1] += 1; *x += 1; diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr new file mode 100644 index 000000000000..7a3824f79a4c --- /dev/null +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr @@ -0,0 +1,17 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/borrowck-unsafe-static-mutable-borrows.rs:19:30 + | +LL | let sfoo: *mut Foo = &mut SFOO; + | ^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | let sfoo: *mut Foo = addr_of_mut!(SFOO); + | ~~~~~~~~~~~~~~~~~~ + +warning: 1 warning emitted + diff --git a/tests/ui/borrowck/issue-20801.rs b/tests/ui/borrowck/issue-20801.rs index c3f136f2876b..ec83af5d5dfc 100644 --- a/tests/ui/borrowck/issue-20801.rs +++ b/tests/ui/borrowck/issue-20801.rs @@ -12,6 +12,7 @@ fn imm_ref() -> &'static T { fn mut_ref() -> &'static mut T { unsafe { &mut GLOBAL_MUT_T } + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] } fn mut_ptr() -> *mut T { diff --git a/tests/ui/borrowck/issue-20801.stderr b/tests/ui/borrowck/issue-20801.stderr index 215bf0100636..b2bee2d88039 100644 --- a/tests/ui/borrowck/issue-20801.stderr +++ b/tests/ui/borrowck/issue-20801.stderr @@ -1,5 +1,20 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/issue-20801.rs:14:14 + | +LL | unsafe { &mut GLOBAL_MUT_T } + | ^^^^^^^^^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | unsafe { addr_of_mut!(GLOBAL_MUT_T) } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + error[E0507]: cannot move out of a mutable reference - --> $DIR/issue-20801.rs:26:22 + --> $DIR/issue-20801.rs:27:22 | LL | let a = unsafe { *mut_ref() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait @@ -11,7 +26,7 @@ LL + let a = unsafe { mut_ref() }; | error[E0507]: cannot move out of a shared reference - --> $DIR/issue-20801.rs:29:22 + --> $DIR/issue-20801.rs:30:22 | LL | let b = unsafe { *imm_ref() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait @@ -23,7 +38,7 @@ LL + let b = unsafe { imm_ref() }; | error[E0507]: cannot move out of a raw pointer - --> $DIR/issue-20801.rs:32:22 + --> $DIR/issue-20801.rs:33:22 | LL | let c = unsafe { *mut_ptr() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait @@ -35,7 +50,7 @@ LL + let c = unsafe { mut_ptr() }; | error[E0507]: cannot move out of a raw pointer - --> $DIR/issue-20801.rs:35:22 + --> $DIR/issue-20801.rs:36:22 | LL | let d = unsafe { *const_ptr() }; | ^^^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait @@ -46,6 +61,6 @@ LL - let d = unsafe { *const_ptr() }; LL + let d = unsafe { const_ptr() }; | -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs index b3cce1b3a061..9b172b413191 100644 --- a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs +++ b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs @@ -8,7 +8,10 @@ mod borrowck_closures_unique { static mut Y: isize = 3; let mut c1 = |y: &'static mut isize| x = y; //~^ ERROR is not declared as mutable - unsafe { c1(&mut Y); } + unsafe { + c1(&mut Y); + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] + } } } @@ -17,36 +20,50 @@ mod borrowck_closures_unique_grandparent { static mut Z: isize = 3; let mut c1 = |z: &'static mut isize| { let mut c2 = |y: &'static mut isize| x = y; - //~^ ERROR is not declared as mutable + //~^ ERROR is not declared as mutable c2(z); }; - unsafe { c1(&mut Z); } + unsafe { + c1(&mut Z); + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] + } } } // adapted from mutability_errors.rs mod mutability_errors { pub fn capture_assign_whole(x: (i32,)) { - || { x = (1,); }; - //~^ ERROR is not declared as mutable + || { + x = (1,); + //~^ ERROR is not declared as mutable + }; } pub fn capture_assign_part(x: (i32,)) { - || { x.0 = 1; }; - //~^ ERROR is not declared as mutable + || { + x.0 = 1; + //~^ ERROR is not declared as mutable + }; } pub fn capture_reborrow_whole(x: (i32,)) { - || { &mut x; }; - //~^ ERROR is not declared as mutable + || { + &mut x; + //~^ ERROR is not declared as mutable + }; } pub fn capture_reborrow_part(x: (i32,)) { - || { &mut x.0; }; - //~^ ERROR is not declared as mutable + || { + &mut x.0; + //~^ ERROR is not declared as mutable + }; } } fn main() { static mut X: isize = 2; - unsafe { borrowck_closures_unique::e(&mut X); } + unsafe { + borrowck_closures_unique::e(&mut X); + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] + } mutability_errors::capture_assign_whole((1000,)); mutability_errors::capture_assign_part((2000,)); diff --git a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr index 4c299cdc455a..e4e4947fce1c 100644 --- a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr +++ b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr @@ -1,3 +1,46 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:12:16 + | +LL | c1(&mut Y); + | ^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | c1(addr_of_mut!(Y)); + | ~~~~~~~~~~~~~~~ + +warning: mutable reference of mutable static is discouraged + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:27:16 + | +LL | c1(&mut Z); + | ^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | c1(addr_of_mut!(Z)); + | ~~~~~~~~~~~~~~~ + +warning: mutable reference of mutable static is discouraged + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:64:37 + | +LL | borrowck_closures_unique::e(&mut X); + | ^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | borrowck_closures_unique::e(addr_of_mut!(X)); + | ~~~~~~~~~~~~~~~ + error[E0594]: cannot assign to `x`, as it is not declared as mutable --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:9:46 | @@ -8,7 +51,7 @@ LL | let mut c1 = |y: &'static mut isize| x = y; | ^^^^^ cannot assign error[E0594]: cannot assign to `x`, as it is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:19:50 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:22:50 | LL | pub fn ee(x: &'static mut isize) { | - help: consider changing this to be mutable: `mut x` @@ -17,38 +60,42 @@ LL | let mut c2 = |y: &'static mut isize| x = y; | ^^^^^ cannot assign error[E0594]: cannot assign to `x`, as it is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:30:14 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:37:13 | LL | pub fn capture_assign_whole(x: (i32,)) { | - help: consider changing this to be mutable: `mut x` -LL | || { x = (1,); }; - | ^^^^^^^^ cannot assign +LL | || { +LL | x = (1,); + | ^^^^^^^^ cannot assign error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:34:14 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:43:13 | LL | pub fn capture_assign_part(x: (i32,)) { | - help: consider changing this to be mutable: `mut x` -LL | || { x.0 = 1; }; - | ^^^^^^^ cannot assign +LL | || { +LL | x.0 = 1; + | ^^^^^^^ cannot assign error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:38:14 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:49:13 | LL | pub fn capture_reborrow_whole(x: (i32,)) { | - help: consider changing this to be mutable: `mut x` -LL | || { &mut x; }; - | ^^^^^^ cannot borrow as mutable +LL | || { +LL | &mut x; + | ^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow `x.0` as mutable, as `x` is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:42:14 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:55:13 | LL | pub fn capture_reborrow_part(x: (i32,)) { | - help: consider changing this to be mutable: `mut x` -LL | || { &mut x.0; }; - | ^^^^^^^^ cannot borrow as mutable +LL | || { +LL | &mut x.0; + | ^^^^^^^^ cannot borrow as mutable -error: aborting due to 6 previous errors +error: aborting due to 6 previous errors; 3 warnings emitted Some errors have detailed explanations: E0594, E0596. For more information about an error, try `rustc --explain E0594`. diff --git a/tests/ui/consts/const_let_assign2.rs b/tests/ui/consts/const_let_assign2.rs index 28265c85dd1f..1c7afe0e3d6c 100644 --- a/tests/ui/consts/const_let_assign2.rs +++ b/tests/ui/consts/const_let_assign2.rs @@ -16,6 +16,7 @@ static mut BB: AA = AA::new(); fn main() { let ptr = unsafe { &mut BB }; + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] for a in ptr.data.iter() { println!("{}", a); } diff --git a/tests/ui/consts/const_let_assign2.stderr b/tests/ui/consts/const_let_assign2.stderr new file mode 100644 index 000000000000..2764153a8a59 --- /dev/null +++ b/tests/ui/consts/const_let_assign2.stderr @@ -0,0 +1,17 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/const_let_assign2.rs:18:24 + | +LL | let ptr = unsafe { &mut BB }; + | ^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | let ptr = unsafe { addr_of_mut!(BB) }; + | ~~~~~~~~~~~~~~~~ + +warning: 1 warning emitted + diff --git a/tests/ui/consts/issue-17718-const-bad-values.rs b/tests/ui/consts/issue-17718-const-bad-values.rs index 62bbb3b569c3..4fedc48452be 100644 --- a/tests/ui/consts/issue-17718-const-bad-values.rs +++ b/tests/ui/consts/issue-17718-const-bad-values.rs @@ -3,7 +3,8 @@ const C1: &'static mut [usize] = &mut []; static mut S: usize = 3; const C2: &'static mut usize = unsafe { &mut S }; -//~^ ERROR: constants cannot refer to statics +//~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] +//~^^ ERROR: constants cannot refer to statics //~| ERROR: constants cannot refer to statics fn main() {} diff --git a/tests/ui/consts/issue-17718-const-bad-values.stderr b/tests/ui/consts/issue-17718-const-bad-values.stderr index 405c2195dec4..2dc91f52669e 100644 --- a/tests/ui/consts/issue-17718-const-bad-values.stderr +++ b/tests/ui/consts/issue-17718-const-bad-values.stderr @@ -1,3 +1,18 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/issue-17718-const-bad-values.rs:5:41 + | +LL | const C2: &'static mut usize = unsafe { &mut S }; + | ^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | const C2: &'static mut usize = unsafe { addr_of_mut!(S) }; + | ~~~~~~~~~~~~~~~ + error[E0764]: mutable references are not allowed in the final value of constants --> $DIR/issue-17718-const-bad-values.rs:1:34 | @@ -21,7 +36,7 @@ LL | const C2: &'static mut usize = unsafe { &mut S }; = help: consider extracting the value of the `static` to a `const`, and referring to that = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 1 warning emitted Some errors have detailed explanations: E0013, E0764. For more information about an error, try `rustc --explain E0013`. diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr index 7960648ce3a1..ed9db6754264 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr @@ -1,3 +1,18 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/const_refers_to_static_cross_crate.rs:13:14 + | +LL | unsafe { &static_cross_crate::ZERO } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | unsafe { addr_of!(static_cross_crate::ZERO) } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static_cross_crate.rs:10:1 | @@ -10,13 +25,13 @@ LL | const SLICE_MUT: &[u8; 1] = { } error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:34:9 + --> $DIR/const_refers_to_static_cross_crate.rs:42:9 | LL | SLICE_MUT => true, | ^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static_cross_crate.rs:15:1 + --> $DIR/const_refers_to_static_cross_crate.rs:17:1 | LL | const U8_MUT: &u8 = { | ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant @@ -27,31 +42,31 @@ LL | const U8_MUT: &u8 = { } error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:42:9 + --> $DIR/const_refers_to_static_cross_crate.rs:50:9 | LL | U8_MUT => true, | ^^^^^^ error[E0080]: evaluation of constant value failed - --> $DIR/const_refers_to_static_cross_crate.rs:22:15 + --> $DIR/const_refers_to_static_cross_crate.rs:25:15 | LL | unsafe { &(*static_cross_crate::ZERO_REF)[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:52:9 + --> $DIR/const_refers_to_static_cross_crate.rs:60:9 | LL | U8_MUT2 => true, | ^^^^^^^ error[E0080]: evaluation of constant value failed - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:59:9 + --> $DIR/const_refers_to_static_cross_crate.rs:67:9 | LL | U8_MUT3 => true, | ^^^^^^^ @@ -59,61 +74,61 @@ LL | U8_MUT3 => true, warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:12:15 + --> $DIR/const_refers_to_static_cross_crate.rs:13:15 | LL | unsafe { &static_cross_crate::ZERO } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:12:15 + --> $DIR/const_refers_to_static_cross_crate.rs:13:15 | LL | unsafe { &static_cross_crate::ZERO } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:17:15 + --> $DIR/const_refers_to_static_cross_crate.rs:20:15 | LL | unsafe { &static_cross_crate::ZERO[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:17:15 + --> $DIR/const_refers_to_static_cross_crate.rs:20:15 | LL | unsafe { &static_cross_crate::ZERO[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:17:15 + --> $DIR/const_refers_to_static_cross_crate.rs:20:15 | LL | unsafe { &static_cross_crate::ZERO[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:22:17 + --> $DIR/const_refers_to_static_cross_crate.rs:25:17 | LL | unsafe { &(*static_cross_crate::ZERO_REF)[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors; 1 warning emitted +error: aborting due to 8 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr index 6ae0b2d1bfef..275323bc286c 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr @@ -1,3 +1,18 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/const_refers_to_static_cross_crate.rs:13:14 + | +LL | unsafe { &static_cross_crate::ZERO } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | unsafe { addr_of!(static_cross_crate::ZERO) } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static_cross_crate.rs:10:1 | @@ -10,13 +25,13 @@ LL | const SLICE_MUT: &[u8; 1] = { } error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:34:9 + --> $DIR/const_refers_to_static_cross_crate.rs:42:9 | LL | SLICE_MUT => true, | ^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static_cross_crate.rs:15:1 + --> $DIR/const_refers_to_static_cross_crate.rs:17:1 | LL | const U8_MUT: &u8 = { | ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant @@ -27,31 +42,31 @@ LL | const U8_MUT: &u8 = { } error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:42:9 + --> $DIR/const_refers_to_static_cross_crate.rs:50:9 | LL | U8_MUT => true, | ^^^^^^ error[E0080]: evaluation of constant value failed - --> $DIR/const_refers_to_static_cross_crate.rs:22:15 + --> $DIR/const_refers_to_static_cross_crate.rs:25:15 | LL | unsafe { &(*static_cross_crate::ZERO_REF)[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:52:9 + --> $DIR/const_refers_to_static_cross_crate.rs:60:9 | LL | U8_MUT2 => true, | ^^^^^^^ error[E0080]: evaluation of constant value failed - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:59:9 + --> $DIR/const_refers_to_static_cross_crate.rs:67:9 | LL | U8_MUT3 => true, | ^^^^^^^ @@ -59,61 +74,61 @@ LL | U8_MUT3 => true, warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:12:15 + --> $DIR/const_refers_to_static_cross_crate.rs:13:15 | LL | unsafe { &static_cross_crate::ZERO } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:12:15 + --> $DIR/const_refers_to_static_cross_crate.rs:13:15 | LL | unsafe { &static_cross_crate::ZERO } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:17:15 + --> $DIR/const_refers_to_static_cross_crate.rs:20:15 | LL | unsafe { &static_cross_crate::ZERO[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:17:15 + --> $DIR/const_refers_to_static_cross_crate.rs:20:15 | LL | unsafe { &static_cross_crate::ZERO[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:17:15 + --> $DIR/const_refers_to_static_cross_crate.rs:20:15 | LL | unsafe { &static_cross_crate::ZERO[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:22:17 + --> $DIR/const_refers_to_static_cross_crate.rs:25:17 | LL | unsafe { &(*static_cross_crate::ZERO_REF)[0] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:27:20 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match static_cross_crate::OPT_ZERO { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors; 1 warning emitted +error: aborting due to 8 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs index bbaa32ddfd1b..3eafa58d9f9f 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs @@ -7,13 +7,16 @@ extern crate static_cross_crate; // Sneaky: reference to a mutable static. // Allowing this would be a disaster for pattern matching, we could violate exhaustiveness checking! -const SLICE_MUT: &[u8; 1] = { //~ ERROR undefined behavior to use this value -//~| encountered a reference pointing to a static variable +const SLICE_MUT: &[u8; 1] = { + //~^ ERROR undefined behavior to use this value + //~| encountered a reference pointing to a static variable unsafe { &static_cross_crate::ZERO } + //~^ WARN shared reference of mutable static is discouraged [static_mut_ref] }; -const U8_MUT: &u8 = { //~ ERROR undefined behavior to use this value -//~| encountered a reference pointing to a static variable +const U8_MUT: &u8 = { + //~^ ERROR undefined behavior to use this value + //~| encountered a reference pointing to a static variable unsafe { &static_cross_crate::ZERO[0] } }; @@ -24,9 +27,14 @@ const U8_MUT2: &u8 = { //~| constant accesses static }; const U8_MUT3: &u8 = { - unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - //~^ ERROR evaluation of constant value failed - //~| constant accesses static + unsafe { + match static_cross_crate::OPT_ZERO { + //~^ ERROR evaluation of constant value failed + //~| constant accesses static + Some(ref u) => u, + None => panic!(), + } + } }; pub fn test(x: &[u8; 1]) -> bool { diff --git a/tests/ui/consts/static_mut_containing_mut_ref.rs b/tests/ui/consts/static_mut_containing_mut_ref.rs index df09c76c5584..874aa59df0bb 100644 --- a/tests/ui/consts/static_mut_containing_mut_ref.rs +++ b/tests/ui/consts/static_mut_containing_mut_ref.rs @@ -3,5 +3,6 @@ static mut STDERR_BUFFER_SPACE: [u8; 42] = [0u8; 42]; pub static mut STDERR_BUFFER: *mut [u8] = unsafe { &mut STDERR_BUFFER_SPACE }; +//~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] fn main() {} diff --git a/tests/ui/consts/static_mut_containing_mut_ref.stderr b/tests/ui/consts/static_mut_containing_mut_ref.stderr new file mode 100644 index 000000000000..56ceba41cf88 --- /dev/null +++ b/tests/ui/consts/static_mut_containing_mut_ref.stderr @@ -0,0 +1,17 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/static_mut_containing_mut_ref.rs:5:52 + | +LL | pub static mut STDERR_BUFFER: *mut [u8] = unsafe { &mut STDERR_BUFFER_SPACE }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | pub static mut STDERR_BUFFER: *mut [u8] = unsafe { addr_of_mut!(STDERR_BUFFER_SPACE) }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +warning: 1 warning emitted + diff --git a/tests/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr b/tests/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr index 3d0de233569c..bc32ecc2c35f 100644 --- a/tests/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr +++ b/tests/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr @@ -1,9 +1,24 @@ -error[E0080]: could not evaluate static initializer - --> $DIR/static_mut_containing_mut_ref2.rs:7:45 +warning: mutable reference of mutable static is discouraged + --> $DIR/static_mut_containing_mut_ref2.rs:8:6 | -LL | pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ modifying a static's initial value from another static's initializer +LL | *(&mut STDERR_BUFFER_SPACE) = 42; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | *addr_of_mut!(STDERR_BUFFER_SPACE) = 42; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 1 previous error +error[E0080]: could not evaluate static initializer + --> $DIR/static_mut_containing_mut_ref2.rs:8:5 + | +LL | *(&mut STDERR_BUFFER_SPACE) = 42; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ modifying a static's initial value from another static's initializer + +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/static_mut_containing_mut_ref2.rs b/tests/ui/consts/static_mut_containing_mut_ref2.rs index 61368546083d..fa79a78eab42 100644 --- a/tests/ui/consts/static_mut_containing_mut_ref2.rs +++ b/tests/ui/consts/static_mut_containing_mut_ref2.rs @@ -4,8 +4,12 @@ static mut STDERR_BUFFER_SPACE: u8 = 0; -pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; }; -//[mut_refs]~^ ERROR could not evaluate static initializer -//[stock]~^^ ERROR mutable references are not allowed in statics +pub static mut STDERR_BUFFER: () = unsafe { + *(&mut STDERR_BUFFER_SPACE) = 42; + //[mut_refs]~^ ERROR could not evaluate static initializer + //[stock]~^^ ERROR mutable references are not allowed in statics + //[mut_refs]~^^^ WARN mutable reference of mutable static is discouraged [static_mut_ref] + //[stock]~^^^^ WARN mutable reference of mutable static is discouraged [static_mut_ref] +}; fn main() {} diff --git a/tests/ui/consts/static_mut_containing_mut_ref2.stock.stderr b/tests/ui/consts/static_mut_containing_mut_ref2.stock.stderr index 3d5b012d42f3..c6e5b07e3b70 100644 --- a/tests/ui/consts/static_mut_containing_mut_ref2.stock.stderr +++ b/tests/ui/consts/static_mut_containing_mut_ref2.stock.stderr @@ -1,12 +1,27 @@ -error[E0658]: mutable references are not allowed in statics - --> $DIR/static_mut_containing_mut_ref2.rs:7:46 +warning: mutable reference of mutable static is discouraged + --> $DIR/static_mut_containing_mut_ref2.rs:8:6 | -LL | pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | *(&mut STDERR_BUFFER_SPACE) = 42; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | *addr_of_mut!(STDERR_BUFFER_SPACE) = 42; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0658]: mutable references are not allowed in statics + --> $DIR/static_mut_containing_mut_ref2.rs:8:6 + | +LL | *(&mut STDERR_BUFFER_SPACE) = 42; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #57349 for more information = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/drop/issue-23338-ensure-param-drop-order.rs b/tests/ui/drop/issue-23338-ensure-param-drop-order.rs index a99f260dde3b..52603744c45f 100644 --- a/tests/ui/drop/issue-23338-ensure-param-drop-order.rs +++ b/tests/ui/drop/issue-23338-ensure-param-drop-order.rs @@ -13,38 +13,39 @@ pub fn main() { d::println("created empty log"); test(&log); - assert_eq!(&log.borrow()[..], - [ - // created empty log - // +-- Make D(da_0, 0) - // | +-- Make D(de_1, 1) - // | | calling foo - // | | entered foo - // | | +-- Make D(de_2, 2) - // | | | +-- Make D(da_1, 3) - // | | | | +-- Make D(de_3, 4) - // | | | | | +-- Make D(de_4, 5) - 3, // | | | +-- Drop D(da_1, 3) - // | | | | | - 4, // | | | +-- Drop D(de_3, 4) - // | | | | - // | | | | eval tail of foo - // | | | +-- Make D(de_5, 6) - // | | | | +-- Make D(de_6, 7) - 5, // | | | | | +-- Drop D(de_4, 5) - // | | | | | - 2, // | | +-- Drop D(de_2, 2) - // | | | | - 6, // | | +-- Drop D(de_5, 6) - // | | | - 1, // | +-- Drop D(de_1, 1) - // | | - 0, // +-- Drop D(da_0, 0) - // | - // | result D(de_6, 7) - 7 // +-- Drop D(de_6, 7) - - ]); + assert_eq!( + &log.borrow()[..], + [ + // created empty log + // +-- Make D(da_0, 0) + // | +-- Make D(de_1, 1) + // | | calling foo + // | | entered foo + // | | +-- Make D(de_2, 2) + // | | | +-- Make D(da_1, 3) + // | | | | +-- Make D(de_3, 4) + // | | | | | +-- Make D(de_4, 5) + 3, // | | | +-- Drop D(da_1, 3) + // | | | | | + 4, // | | | +-- Drop D(de_3, 4) + // | | | | + // | | | | eval tail of foo + // | | | +-- Make D(de_5, 6) + // | | | | +-- Make D(de_6, 7) + 5, // | | | | | +-- Drop D(de_4, 5) + // | | | | | + 2, // | | +-- Drop D(de_2, 2) + // | | | | + 6, // | | +-- Drop D(de_5, 6) + // | | | + 1, // | +-- Drop D(de_1, 1) + // | | + 0, // +-- Drop D(da_0, 0) + // | + // | result D(de_6, 7) + 7 // +-- Drop D(de_6, 7) + ] + ); } fn test<'a>(log: d::Log<'a>) { @@ -57,13 +58,13 @@ fn test<'a>(log: d::Log<'a>) { fn foo<'a>(da0: D<'a>, de1: D<'a>) -> D<'a> { d::println("entered foo"); - let de2 = de1.incr(); // creates D(de_2, 2) + let de2 = de1.incr(); // creates D(de_2, 2) let de4 = { let _da1 = da0.incr(); // creates D(da_1, 3) - de2.incr().incr() // creates D(de_3, 4) and D(de_4, 5) + de2.incr().incr() // creates D(de_3, 4) and D(de_4, 5) }; d::println("eval tail of foo"); - de4.incr().incr() // creates D(de_5, 6) and D(de_6, 7) + de4.incr().incr() // creates D(de_5, 6) and D(de_6, 7) } // This module provides simultaneous printouts of the dynamic extents @@ -74,9 +75,9 @@ const PREF_INDENT: u32 = 16; pub mod d { #![allow(unused_parens)] + use std::cell::RefCell; use std::fmt; use std::mem; - use std::cell::RefCell; static mut counter: u32 = 0; static mut trails: u64 = 0; @@ -89,7 +90,8 @@ pub mod d { pub fn max_width() -> u32 { unsafe { - (mem::size_of_val(&trails)*8) as u32 + (mem::size_of_val(&trails) * 8) as u32 + //~^ WARN shared reference of mutable static is discouraged [static_mut_ref] } } @@ -123,7 +125,11 @@ pub mod d { } pub struct D<'a> { - name: &'static str, i: u32, uid: u32, trail: u32, log: Log<'a> + name: &'static str, + i: u32, + uid: u32, + trail: u32, + log: Log<'a>, } impl<'a> fmt::Display for D<'a> { @@ -139,9 +145,7 @@ pub mod d { let ctr = counter; counter += 1; trails |= (1 << trail); - let ret = D { - name: name, i: i, log: log, uid: ctr, trail: trail - }; + let ret = D { name: name, i: i, log: log, uid: ctr, trail: trail }; indent_println(trail, &format!("+-- Make {}", ret)); ret } @@ -153,7 +157,9 @@ pub mod d { impl<'a> Drop for D<'a> { fn drop(&mut self) { - unsafe { trails &= !(1 << self.trail); }; + unsafe { + trails &= !(1 << self.trail); + }; self.log.borrow_mut().push(self.uid); indent_println(self.trail, &format!("+-- Drop {}", self)); indent_println(::PREF_INDENT, ""); diff --git a/tests/ui/drop/issue-23338-ensure-param-drop-order.stderr b/tests/ui/drop/issue-23338-ensure-param-drop-order.stderr new file mode 100644 index 000000000000..fd36ccbcbee4 --- /dev/null +++ b/tests/ui/drop/issue-23338-ensure-param-drop-order.stderr @@ -0,0 +1,17 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/issue-23338-ensure-param-drop-order.rs:93:31 + | +LL | (mem::size_of_val(&trails) * 8) as u32 + | ^^^^^^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | (mem::size_of_val(addr_of!(trails)) * 8) as u32 + | ~~~~~~~~~~~~~~~~ + +warning: 1 warning emitted + diff --git a/tests/ui/error-codes/E0017.rs b/tests/ui/error-codes/E0017.rs index c211ad1a2f8f..9d3433fa543f 100644 --- a/tests/ui/error-codes/E0017.rs +++ b/tests/ui/error-codes/E0017.rs @@ -3,12 +3,16 @@ const C: i32 = 2; static mut M: i32 = 3; const CR: &'static mut i32 = &mut C; //~ ERROR mutable references are not allowed - //~| WARN taking a mutable +//~| WARN taking a mutable + static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0658 - //~| ERROR cannot borrow - //~| ERROR mutable references are not allowed +//~| ERROR cannot borrow +//~| ERROR mutable references are not allowed static CONST_REF: &'static mut i32 = &mut C; //~ ERROR mutable references are not allowed - //~| WARN taking a mutable +//~| WARN taking a mutable + static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR mutable references are not +//~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] + fn main() {} diff --git a/tests/ui/error-codes/E0017.stderr b/tests/ui/error-codes/E0017.stderr index 6e48f9582f1c..ea6055da1c1f 100644 --- a/tests/ui/error-codes/E0017.stderr +++ b/tests/ui/error-codes/E0017.stderr @@ -1,3 +1,18 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/E0017.rs:15:52 + | +LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; + | ^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { addr_of_mut!(M) }; + | ~~~~~~~~~~~~~~~ + warning: taking a mutable reference to a `const` item --> $DIR/E0017.rs:5:30 | @@ -20,7 +35,7 @@ LL | const CR: &'static mut i32 = &mut C; | ^^^^^^ error[E0658]: mutation through a reference is not allowed in statics - --> $DIR/E0017.rs:7:39 + --> $DIR/E0017.rs:8:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ @@ -29,19 +44,19 @@ LL | static STATIC_REF: &'static mut i32 = &mut X; = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0764]: mutable references are not allowed in the final value of statics - --> $DIR/E0017.rs:7:39 + --> $DIR/E0017.rs:8:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ error[E0596]: cannot borrow immutable static item `X` as mutable - --> $DIR/E0017.rs:7:39 + --> $DIR/E0017.rs:8:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ cannot borrow as mutable warning: taking a mutable reference to a `const` item - --> $DIR/E0017.rs:11:38 + --> $DIR/E0017.rs:12:38 | LL | static CONST_REF: &'static mut i32 = &mut C; | ^^^^^^ @@ -55,18 +70,18 @@ LL | const C: i32 = 2; | ^^^^^^^^^^^^ error[E0764]: mutable references are not allowed in the final value of statics - --> $DIR/E0017.rs:11:38 + --> $DIR/E0017.rs:12:38 | LL | static CONST_REF: &'static mut i32 = &mut C; | ^^^^^^ error[E0764]: mutable references are not allowed in the final value of statics - --> $DIR/E0017.rs:13:52 + --> $DIR/E0017.rs:15:52 | LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; | ^^^^^^ -error: aborting due to 6 previous errors; 2 warnings emitted +error: aborting due to 6 previous errors; 3 warnings emitted Some errors have detailed explanations: E0596, E0658, E0764. For more information about an error, try `rustc --explain E0596`. diff --git a/tests/ui/issues/issue-20616.rs b/tests/ui/impl-header-lifetime-elision/bare_type.rs similarity index 82% rename from tests/ui/issues/issue-20616.rs rename to tests/ui/impl-header-lifetime-elision/bare_type.rs index 6c24d437272e..9af98f870d2d 100644 --- a/tests/ui/issues/issue-20616.rs +++ b/tests/ui/impl-header-lifetime-elision/bare_type.rs @@ -33,12 +33,11 @@ type TypeI = T; static STATIC: () = (); fn main() { - // ensure token `>=` works fine - let _: TypeA<'static>= &STATIC; - let _: TypeA<'static,>= &STATIC; + let _: TypeA<'static> = &STATIC; + let _: TypeA<'static,> = &STATIC; // ensure token `>>=` works fine - let _: Box>= Box::new(&STATIC); - let _: Box>= Box::new(&STATIC); + let _: Box> = Box::new(&STATIC); + let _: Box> = Box::new(&STATIC); } diff --git a/tests/ui/issues/issue-23611-enum-swap-in-drop.rs b/tests/ui/issues/issue-23611-enum-swap-in-drop.rs index cdb130d600c5..b967e6aecdd4 100644 --- a/tests/ui/issues/issue-23611-enum-swap-in-drop.rs +++ b/tests/ui/issues/issue-23611-enum-swap-in-drop.rs @@ -37,11 +37,9 @@ pub fn main() { // | | | +-- Make D(g_b_5, 50000005) // | | | | in g_B(b4b2) from GaspB::drop // | | | +-- Drop D(g_b_5, 50000005) - 50000005, - // | | | + 50000005, // | | | // | | +-- Drop D(GaspB::drop_3, 30000004) - 30000004, - // | | + 30000004, // | | // +-- Drop D(test_1, 10000000) 10000000, // | @@ -49,15 +47,13 @@ pub fn main() { // | | +-- Make D(f_a_4, 40000007) // | | | in f_A(a3a0) from GaspA::drop // | | +-- Drop D(f_a_4, 40000007) - 40000007, - // | | + 40000007, // | | // +-- Drop D(GaspA::drop_2, 20000006) - 20000006, - // | + 20000006, // | // +-- Drop D(drop_6, 60000002) - 60000002 - // - ]); + 60000002 // + ] + ); // For reference purposes, the old (incorrect) behavior would produce the following // output, which you can compare to the above: @@ -106,8 +102,8 @@ fn test<'a>(log: d::Log<'a>) { let _e = E::B(GaspB(g_b, 0xB4B0, log, D::new("test", 1, log)), true); } -struct GaspA<'a>(for <'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>); -struct GaspB<'a>(for <'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>); +struct GaspA<'a>(for<'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>); +struct GaspB<'a>(for<'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>); impl<'a> Drop for GaspA<'a> { fn drop(&mut self) { @@ -124,7 +120,8 @@ impl<'a> Drop for GaspB<'a> { } enum E<'a> { - A(GaspA<'a>, bool), B(GaspB<'a>, bool), + A(GaspA<'a>, bool), + B(GaspB<'a>, bool), } fn f_a(x: u32, ctxt: &str, log: d::Log) { @@ -174,9 +171,9 @@ const PREF_INDENT: u32 = 20; pub mod d { #![allow(unused_parens)] + use std::cell::RefCell; use std::fmt; use std::mem; - use std::cell::RefCell; static mut counter: u16 = 0; static mut trails: u64 = 0; @@ -189,7 +186,8 @@ pub mod d { pub fn max_width() -> u32 { unsafe { - (mem::size_of_val(&trails)*8) as u32 + (mem::size_of_val(&trails) * 8) as u32 + //~^ WARN shared reference of mutable static is discouraged [static_mut_ref] } } @@ -223,7 +221,11 @@ pub mod d { } pub struct D<'a> { - name: &'static str, i: u8, uid: u32, trail: u32, log: Log<'a> + name: &'static str, + i: u8, + uid: u32, + trail: u32, + log: Log<'a>, } impl<'a> fmt::Display for D<'a> { @@ -239,9 +241,7 @@ pub mod d { let ctr = ((i as u32) * 10_000_000) + (counter as u32); counter += 1; trails |= (1 << trail); - let ret = D { - name: name, i: i, log: log, uid: ctr, trail: trail - }; + let ret = D { name: name, i: i, log: log, uid: ctr, trail: trail }; indent_println(trail, &format!("+-- Make {}", ret)); ret } @@ -250,7 +250,9 @@ pub mod d { impl<'a> Drop for D<'a> { fn drop(&mut self) { - unsafe { trails &= !(1 << self.trail); }; + unsafe { + trails &= !(1 << self.trail); + }; self.log.borrow_mut().push(self.uid); indent_println(self.trail, &format!("+-- Drop {}", self)); indent_println(::PREF_INDENT, ""); diff --git a/tests/ui/issues/issue-23611-enum-swap-in-drop.stderr b/tests/ui/issues/issue-23611-enum-swap-in-drop.stderr new file mode 100644 index 000000000000..14a986a33326 --- /dev/null +++ b/tests/ui/issues/issue-23611-enum-swap-in-drop.stderr @@ -0,0 +1,17 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/issue-23611-enum-swap-in-drop.rs:189:31 + | +LL | (mem::size_of_val(&trails) * 8) as u32 + | ^^^^^^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | (mem::size_of_val(addr_of!(trails)) * 8) as u32 + | ~~~~~~~~~~~~~~~~ + +warning: 1 warning emitted + diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs index 7d3b00dfc716..8eb544e8ab9d 100644 --- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs @@ -14,9 +14,8 @@ struct S1 { impl S1 { fn new(_x: u64) -> S1 { - S1 { - a: unsafe { &mut X1 }, - } + S1 { a: unsafe { &mut X1 } } + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] } } diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr new file mode 100644 index 000000000000..17217cd5859d --- /dev/null +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr @@ -0,0 +1,17 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/borrowck-thread-local-static-mut-borrow-outlives-fn.rs:17:26 + | +LL | S1 { a: unsafe { &mut X1 } } + | ^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | S1 { a: unsafe { addr_of_mut!(X1) } } + | ~~~~~~~~~~~~~~~~ + +warning: 1 warning emitted + diff --git a/tests/ui/static/reference-of-mut-static-safe.e2021.stderr b/tests/ui/static/reference-of-mut-static-safe.e2021.stderr index c38fe8790637..16f47ace3a93 100644 --- a/tests/ui/static/reference-of-mut-static-safe.e2021.stderr +++ b/tests/ui/static/reference-of-mut-static-safe.e2021.stderr @@ -14,10 +14,10 @@ LL | let _x = addr_of!(X); | ~~~~~~~~~~~ error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/reference-of-mut-static-safe.rs:9:14 + --> $DIR/reference-of-mut-static-safe.rs:9:15 | LL | let _x = &X; - | ^^ use of mutable static + | ^ use of mutable static | = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior diff --git a/tests/ui/static/safe-extern-statics-mut.rs b/tests/ui/static/safe-extern-statics-mut.rs index 324fa443aa50..1c0662e0a6ce 100644 --- a/tests/ui/static/safe-extern-statics-mut.rs +++ b/tests/ui/static/safe-extern-statics-mut.rs @@ -10,6 +10,8 @@ extern "C" { fn main() { let b = B; //~ ERROR use of mutable static is unsafe let rb = &B; //~ ERROR use of mutable static is unsafe + //~^ WARN shared reference of mutable static is discouraged [static_mut_ref] let xb = XB; //~ ERROR use of mutable static is unsafe let xrb = &XB; //~ ERROR use of mutable static is unsafe + //~^ WARN shared reference of mutable static is discouraged [static_mut_ref] } diff --git a/tests/ui/static/safe-extern-statics-mut.stderr b/tests/ui/static/safe-extern-statics-mut.stderr index e390625f20a9..eda353ce6736 100644 --- a/tests/ui/static/safe-extern-statics-mut.stderr +++ b/tests/ui/static/safe-extern-statics-mut.stderr @@ -1,3 +1,32 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/safe-extern-statics-mut.rs:12:14 + | +LL | let rb = &B; + | ^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let rb = addr_of!(B); + | ~~~~~~~~~~~ + +warning: shared reference of mutable static is discouraged + --> $DIR/safe-extern-statics-mut.rs:15:15 + | +LL | let xrb = &XB; + | ^^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | let xrb = addr_of!(XB); + | ~~~~~~~~~~~~ + error[E0133]: use of mutable static is unsafe and requires unsafe function or block --> $DIR/safe-extern-statics-mut.rs:11:13 | @@ -15,7 +44,7 @@ LL | let rb = &B; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics-mut.rs:13:14 + --> $DIR/safe-extern-statics-mut.rs:14:14 | LL | let xb = XB; | ^^ use of mutable static @@ -23,13 +52,13 @@ LL | let xb = XB; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics-mut.rs:14:16 + --> $DIR/safe-extern-statics-mut.rs:15:16 | LL | let xrb = &XB; | ^^ use of mutable static | = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/statics/issue-15261.rs b/tests/ui/statics/issue-15261.rs index ec413f6d1d2b..14422329b7dc 100644 --- a/tests/ui/statics/issue-15261.rs +++ b/tests/ui/statics/issue-15261.rs @@ -6,6 +6,7 @@ static mut n_mut: usize = 0; -static n: &'static usize = unsafe{ &n_mut }; +static n: &'static usize = unsafe { &n_mut }; +//~^ WARN shared reference of mutable static is discouraged [static_mut_ref] fn main() {} diff --git a/tests/ui/statics/issue-15261.stderr b/tests/ui/statics/issue-15261.stderr new file mode 100644 index 000000000000..72d88ce1b383 --- /dev/null +++ b/tests/ui/statics/issue-15261.stderr @@ -0,0 +1,17 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/issue-15261.rs:9:37 + | +LL | static n: &'static usize = unsafe { &n_mut }; + | ^^^^^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | static n: &'static usize = unsafe { addr_of!(n_mut) }; + | ~~~~~~~~~~~~~~~ + +warning: 1 warning emitted + diff --git a/tests/ui/statics/static-mut-xc.rs b/tests/ui/statics/static-mut-xc.rs index 1d172d26a594..2fc265e02eaa 100644 --- a/tests/ui/statics/static-mut-xc.rs +++ b/tests/ui/statics/static-mut-xc.rs @@ -7,7 +7,6 @@ // aux-build:static_mut_xc.rs - extern crate static_mut_xc; unsafe fn static_bound(_: &'static isize) {} @@ -27,7 +26,9 @@ unsafe fn run() { static_mut_xc::a = -3; assert_eq!(static_mut_xc::a, -3); static_bound(&static_mut_xc::a); + //~^ WARN shared reference of mutable static is discouraged [static_mut_ref] static_bound_set(&mut static_mut_xc::a); + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] } pub fn main() { diff --git a/tests/ui/statics/static-mut-xc.stderr b/tests/ui/statics/static-mut-xc.stderr new file mode 100644 index 000000000000..37aa336bc50f --- /dev/null +++ b/tests/ui/statics/static-mut-xc.stderr @@ -0,0 +1,31 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/static-mut-xc.rs:28:18 + | +LL | static_bound(&static_mut_xc::a); + | ^^^^^^^^^^^^^^^^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | static_bound(addr_of!(static_mut_xc::a)); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +warning: mutable reference of mutable static is discouraged + --> $DIR/static-mut-xc.rs:30:22 + | +LL | static_bound_set(&mut static_mut_xc::a); + | ^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | static_bound_set(addr_of_mut!(static_mut_xc::a)); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +warning: 2 warnings emitted + diff --git a/tests/ui/statics/static-recursive.rs b/tests/ui/statics/static-recursive.rs index 95dadc81f811..216beb0206d9 100644 --- a/tests/ui/statics/static-recursive.rs +++ b/tests/ui/statics/static-recursive.rs @@ -1,36 +1,43 @@ // run-pass + static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; +//~^ WARN shared reference of mutable static is discouraged [static_mut_ref] struct StaticDoubleLinked { prev: &'static StaticDoubleLinked, next: &'static StaticDoubleLinked, data: i32, - head: bool + head: bool, } -static L1: StaticDoubleLinked = StaticDoubleLinked{prev: &L3, next: &L2, data: 1, head: true}; -static L2: StaticDoubleLinked = StaticDoubleLinked{prev: &L1, next: &L3, data: 2, head: false}; -static L3: StaticDoubleLinked = StaticDoubleLinked{prev: &L2, next: &L1, data: 3, head: false}; - +static L1: StaticDoubleLinked = StaticDoubleLinked { prev: &L3, next: &L2, data: 1, head: true }; +static L2: StaticDoubleLinked = StaticDoubleLinked { prev: &L1, next: &L3, data: 2, head: false }; +static L3: StaticDoubleLinked = StaticDoubleLinked { prev: &L2, next: &L1, data: 3, head: false }; pub fn main() { - unsafe { assert_eq!(S, *(S as *const *const u8)); } + unsafe { + assert_eq!(S, *(S as *const *const u8)); + } let mut test_vec = Vec::new(); let mut cur = &L1; loop { test_vec.push(cur.data); cur = cur.next; - if cur.head { break } + if cur.head { + break; + } } - assert_eq!(&test_vec, &[1,2,3]); + assert_eq!(&test_vec, &[1, 2, 3]); let mut test_vec = Vec::new(); let mut cur = &L1; loop { cur = cur.prev; test_vec.push(cur.data); - if cur.head { break } + if cur.head { + break; + } } - assert_eq!(&test_vec, &[3,2,1]); + assert_eq!(&test_vec, &[3, 2, 1]); } diff --git a/tests/ui/statics/static-recursive.stderr b/tests/ui/statics/static-recursive.stderr new file mode 100644 index 000000000000..15888e5c68d8 --- /dev/null +++ b/tests/ui/statics/static-recursive.stderr @@ -0,0 +1,17 @@ +warning: shared reference of mutable static is discouraged + --> $DIR/static-recursive.rs:3:36 + | +LL | static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; + | ^^ shared reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer + | +LL | static mut S: *const u8 = unsafe { addr_of!(S) as *const *const u8 as *const u8 }; + | ~~~~~~~~~~~ + +warning: 1 warning emitted + diff --git a/tests/ui/thread-local/thread-local-static.rs b/tests/ui/thread-local/thread-local-static.rs index e6cd4839dda7..f5fb09848974 100644 --- a/tests/ui/thread-local/thread-local-static.rs +++ b/tests/ui/thread-local/thread-local-static.rs @@ -8,7 +8,8 @@ static mut STATIC_VAR_2: [u32; 8] = [4; 8]; const fn g(x: &mut [u32; 8]) { //~^ ERROR mutable references are not allowed std::mem::swap(x, &mut STATIC_VAR_2) - //~^ ERROR thread-local statics cannot be accessed + //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref] + //~^^ ERROR thread-local statics cannot be accessed //~| ERROR mutable references are not allowed //~| ERROR use of mutable static is unsafe //~| constant functions cannot refer to statics diff --git a/tests/ui/thread-local/thread-local-static.stderr b/tests/ui/thread-local/thread-local-static.stderr index c1777dd60db6..b03f4580c2cf 100644 --- a/tests/ui/thread-local/thread-local-static.stderr +++ b/tests/ui/thread-local/thread-local-static.stderr @@ -1,3 +1,18 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/thread-local-static.rs:10:23 + | +LL | std::mem::swap(x, &mut STATIC_VAR_2) + | ^^^^^^^^^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | std::mem::swap(x, addr_of_mut!(STATIC_VAR_2)) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + error[E0133]: use of mutable static is unsafe and requires unsafe function or block --> $DIR/thread-local-static.rs:10:28 | @@ -38,7 +53,7 @@ LL | std::mem::swap(x, &mut STATIC_VAR_2) = note: see issue #57349 for more information = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable -error: aborting due to 5 previous errors +error: aborting due to 5 previous errors; 1 warning emitted Some errors have detailed explanations: E0013, E0133, E0625, E0658. For more information about an error, try `rustc --explain E0013`. diff --git a/tests/ui/thread-local/thread-local-static.thir.stderr b/tests/ui/thread-local/thread-local-static.thir.stderr new file mode 100644 index 000000000000..2043b268c090 --- /dev/null +++ b/tests/ui/thread-local/thread-local-static.thir.stderr @@ -0,0 +1,59 @@ +warning: mutable reference of mutable static is discouraged + --> $DIR/thread-local-static.rs:12:23 + | +LL | std::mem::swap(x, &mut STATIC_VAR_2) + | ^^^^^^^^^^^^^^^^^ mutable reference of mutable static + | + = note: for more information, see issue #114447 + = note: reference of mutable static is a hard error from 2024 edition + = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + = note: `#[warn(static_mut_ref)]` on by default +help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer + | +LL | std::mem::swap(x, addr_of_mut!(STATIC_VAR_2)) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0658]: mutable references are not allowed in constant functions + --> $DIR/thread-local-static.rs:10:12 + | +LL | const fn g(x: &mut [u32; 8]) { + | ^ + | + = note: see issue #57349 for more information + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + +error[E0625]: thread-local statics cannot be accessed at compile-time + --> $DIR/thread-local-static.rs:12:28 + | +LL | std::mem::swap(x, &mut STATIC_VAR_2) + | ^^^^^^^^^^^^ + +error[E0013]: constant functions cannot refer to statics + --> $DIR/thread-local-static.rs:12:28 + | +LL | std::mem::swap(x, &mut STATIC_VAR_2) + | ^^^^^^^^^^^^ + | + = help: consider extracting the value of the `static` to a `const`, and referring to that + +error[E0658]: mutable references are not allowed in constant functions + --> $DIR/thread-local-static.rs:12:23 + | +LL | std::mem::swap(x, &mut STATIC_VAR_2) + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #57349 for more information + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/thread-local-static.rs:12:23 + | +LL | std::mem::swap(x, &mut STATIC_VAR_2) + | ^^^^^^^^^^^^^^^^^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error: aborting due to 5 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0013, E0133, E0625, E0658. +For more information about an error, try `rustc --explain E0013`. From 0f69276e60cd9636e6ad2c76dcc9f68619cc6851 Mon Sep 17 00:00:00 2001 From: homersimpsons Date: Sun, 7 Jan 2024 15:38:40 +0100 Subject: [PATCH 154/257] line-index: Update README.md with suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Laurențiu Nicola --- lib/line-index/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/line-index/README.md b/lib/line-index/README.md index c0d310c857cd..93ac03696a0d 100644 --- a/lib/line-index/README.md +++ b/lib/line-index/README.md @@ -1,8 +1,8 @@ # line-index -This crate is developped as part of `rust-analyzer`. +This crate is developed as part of `rust-analyzer`. -line-index is a library to convert between text offset and its corresponding line/column. +line-index is a library to convert between text offsets and corresponding line/column coordinates. ## Installation @@ -10,7 +10,9 @@ To add this crate to a project simply run `cargo add line-index`. ## Usage -The main structure is `LineIndex`. It is constructed with an utf-8 text then various utility functions can be used on it. +The main structure is `LineIndex`. + +It is constructed with an UTF-8 string, but also supports UTF-16 and UTF-32 offsets. ### Example @@ -25,4 +27,4 @@ line_index.offset(LineCol { line: 2, col: 3 }); // Some (24) ## SemVer -This crate follows [semver principles]([url](https://semver.org/)https://semver.org/). +This crate uses [semver](https://semver.org/) versioning. From 4071572cb416a1740e5530130b0454f9ce2db869 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 7 Jan 2024 14:59:59 +0000 Subject: [PATCH 155/257] Merge dead bb pruning and unreachable bb deduplication. --- compiler/rustc_middle/src/mir/mod.rs | 1 + .../rustc_mir_transform/src/const_goto.rs | 2 +- .../src/deduplicate_blocks.rs | 2 +- .../src/early_otherwise_branch.rs | 2 +- compiler/rustc_mir_transform/src/inline.rs | 5 +- .../rustc_mir_transform/src/match_branches.rs | 2 +- .../src/remove_unneeded_drops.rs | 2 +- .../src/separate_const_switch.rs | 4 +- compiler/rustc_mir_transform/src/simplify.rs | 92 ++++++++----------- ...await.b-{closure#0}.coroutine_resume.0.mir | 6 +- ...ng.identity.JumpThreading.panic-abort.diff | 10 +- ...g.identity.JumpThreading.panic-unwind.diff | 10 +- tests/mir-opt/jump_threading.rs | 8 +- ...d_constant.main.GVN.32bit.panic-abort.diff | 24 ++--- ..._constant.main.GVN.32bit.panic-unwind.diff | 18 ++-- ...d_constant.main.GVN.64bit.panic-abort.diff | 24 ++--- ..._constant.main.GVN.64bit.panic-unwind.diff | 18 ++-- ...t_switch.identity.SeparateConstSwitch.diff | 24 ++--- ...witch.too_complex.SeparateConstSwitch.diff | 36 ++++---- 19 files changed, 129 insertions(+), 161 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index d426f6d8969a..36f5ba161d5f 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1316,6 +1316,7 @@ impl<'tcx> BasicBlockData<'tcx> { } /// Does the block have no statements and an unreachable terminator? + #[inline] pub fn is_empty_unreachable(&self) -> bool { self.statements.is_empty() && matches!(self.terminator().kind, TerminatorKind::Unreachable) } diff --git a/compiler/rustc_mir_transform/src/const_goto.rs b/compiler/rustc_mir_transform/src/const_goto.rs index 3884346076ee..cb5b66b314d6 100644 --- a/compiler/rustc_mir_transform/src/const_goto.rs +++ b/compiler/rustc_mir_transform/src/const_goto.rs @@ -51,7 +51,7 @@ impl<'tcx> MirPass<'tcx> for ConstGoto { // if we applied optimizations, we potentially have some cfg to cleanup to // make it easier for further passes if should_simplify { - simplify_cfg(tcx, body); + simplify_cfg(body); simplify_locals(body, tcx); } } diff --git a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs index b40b2ec8bfdc..824974970bb3 100644 --- a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs +++ b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs @@ -25,7 +25,7 @@ impl<'tcx> MirPass<'tcx> for DeduplicateBlocks { if has_opts_to_apply { let mut opt_applier = OptApplier { tcx, duplicates }; opt_applier.visit_body(body); - simplify_cfg(tcx, body); + simplify_cfg(body); } } } diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 6eb6cb069fec..0d600f0f937d 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -212,7 +212,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { // Since this optimization adds new basic blocks and invalidates others, // clean up the cfg to make it nicer for other passes if should_cleanup { - simplify_cfg(tcx, body); + simplify_cfg(body); } } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 8ad804bf3e7a..ebefc3b47cc4 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -15,7 +15,7 @@ use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; use crate::cost_checker::CostChecker; -use crate::simplify::{remove_dead_blocks, CfgSimplifier}; +use crate::simplify::simplify_cfg; use crate::util; use std::iter; use std::ops::{Range, RangeFrom}; @@ -56,8 +56,7 @@ impl<'tcx> MirPass<'tcx> for Inline { let _guard = span.enter(); if inline(tcx, body) { debug!("running simplify cfg on {:?}", body.source); - CfgSimplifier::new(body).simplify(); - remove_dead_blocks(body); + simplify_cfg(body); deref_finder(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 1c4aa37d57ff..6d4332793af3 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -174,7 +174,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { } if should_cleanup { - simplify_cfg(tcx, body); + simplify_cfg(body); } } } diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 5d528bed3564..2778d91e17b9 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -38,7 +38,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops { // if we applied optimizations, we potentially have some cfg to cleanup to // make it easier for further passes if should_simplify { - simplify_cfg(tcx, body); + simplify_cfg(body); } } } diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs index 6e22690d8dad..7120ef721427 100644 --- a/compiler/rustc_mir_transform/src/separate_const_switch.rs +++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs @@ -50,11 +50,11 @@ impl<'tcx> MirPass<'tcx> for SeparateConstSwitch { sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts } - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // If execution did something, applying a simplification layer // helps later passes optimize the copy away. if separate_const_switch(body) > 0 { - super::simplify::simplify_cfg(tcx, body); + super::simplify::simplify_cfg(body); } } } diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 856a0f227714..8c8818bd68e6 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -27,7 +27,6 @@ //! naively generate still contains the `_a = ()` write in the unreachable block "after" the //! return. -use rustc_data_structures::fx::FxIndexSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -62,9 +61,8 @@ impl SimplifyCfg { } } -pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +pub(crate) fn simplify_cfg(body: &mut Body<'_>) { CfgSimplifier::new(body).simplify(); - remove_duplicate_unreachable_blocks(tcx, body); remove_dead_blocks(body); // FIXME: Should probably be moved into some kind of pass manager @@ -76,9 +74,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { self.name() } - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source); - simplify_cfg(tcx, body); + simplify_cfg(body); } } @@ -289,55 +287,25 @@ pub fn simplify_duplicate_switch_targets(terminator: &mut Terminator<'_>) { } } -pub fn remove_duplicate_unreachable_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - struct OptApplier<'tcx> { - tcx: TyCtxt<'tcx>, - duplicates: FxIndexSet, - } +pub(crate) fn remove_dead_blocks(body: &mut Body<'_>) { + let should_deduplicate_unreachable = |bbdata: &BasicBlockData<'_>| { + // CfgSimplifier::simplify leaves behind some unreachable basic blocks without a + // terminator. Those blocks will be deleted by remove_dead_blocks, but we run just + // before then so we need to handle missing terminators. + // We also need to prevent confusing cleanup and non-cleanup blocks. In practice we + // don't emit empty unreachable cleanup blocks, so this simple check suffices. + bbdata.terminator.is_some() && bbdata.is_empty_unreachable() && !bbdata.is_cleanup + }; - impl<'tcx> MutVisitor<'tcx> for OptApplier<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { - for target in terminator.successors_mut() { - // We don't have to check whether `target` is a cleanup block, because have - // entirely excluded cleanup blocks in building the set of duplicates. - if self.duplicates.contains(target) { - *target = self.duplicates[0]; - } - } - - simplify_duplicate_switch_targets(terminator); - - self.super_terminator(terminator, location); - } - } - - let unreachable_blocks = body + let reachable = traversal::reachable_as_bitset(body); + let empty_unreachable_blocks = body .basic_blocks .iter_enumerated() - .filter(|(_, bb)| { - // CfgSimplifier::simplify leaves behind some unreachable basic blocks without a - // terminator. Those blocks will be deleted by remove_dead_blocks, but we run just - // before then so we need to handle missing terminators. - // We also need to prevent confusing cleanup and non-cleanup blocks. In practice we - // don't emit empty unreachable cleanup blocks, so this simple check suffices. - bb.terminator.is_some() && bb.is_empty_unreachable() && !bb.is_cleanup - }) - .map(|(block, _)| block) - .collect::>(); + .filter(|(bb, bbdata)| should_deduplicate_unreachable(bbdata) && reachable.contains(*bb)) + .count(); - if unreachable_blocks.len() > 1 { - OptApplier { tcx, duplicates: unreachable_blocks }.visit_body(body); - } -} - -pub fn remove_dead_blocks(body: &mut Body<'_>) { - let reachable = traversal::reachable_as_bitset(body); let num_blocks = body.basic_blocks.len(); - if num_blocks == reachable.count() { + if num_blocks == reachable.count() && empty_unreachable_blocks <= 1 { return; } @@ -346,14 +314,28 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) { let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); let mut orig_index = 0; let mut used_index = 0; - basic_blocks.raw.retain(|_| { - let keep = reachable.contains(BasicBlock::new(orig_index)); - if keep { - replacements[orig_index] = BasicBlock::new(used_index); - used_index += 1; + let mut kept_unreachable = None; + basic_blocks.raw.retain(|bbdata| { + let orig_bb = BasicBlock::new(orig_index); + if !reachable.contains(orig_bb) { + orig_index += 1; + return false; } + + let used_bb = BasicBlock::new(used_index); + if should_deduplicate_unreachable(bbdata) { + let kept_unreachable = *kept_unreachable.get_or_insert(used_bb); + if kept_unreachable != used_bb { + replacements[orig_index] = kept_unreachable; + orig_index += 1; + return false; + } + } + + replacements[orig_index] = used_bb; + used_index += 1; orig_index += 1; - keep + true }); for block in basic_blocks { diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index 3a9c80caa1e6..3c0d4008c901 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -108,7 +108,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>, bb0: { _39 = discriminant((*(_1.0: &mut {async fn body@$DIR/async_await.rs:15:18: 18:2}))); - switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb30]; + switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb9]; } bb1: { @@ -345,8 +345,4 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>, bb29: { assert(const false, "`async fn` resumed after completion") -> [success: bb29, unwind unreachable]; } - - bb30: { - unreachable; - } } diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff index f50603a66a56..f04ca72dd6d9 100644 --- a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff +++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff @@ -56,7 +56,7 @@ StorageLive(_11); StorageLive(_12); _10 = discriminant(_4); - switchInt(move _10) -> [0: bb8, 1: bb6, otherwise: bb7]; + switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb2]; } bb1: { @@ -114,20 +114,16 @@ _3 = ControlFlow::, i32>::Break(move _13); StorageDead(_13); - goto -> bb5; -+ goto -> bb9; ++ goto -> bb8; } bb7: { - unreachable; - } - - bb8: { _11 = move ((_4 as Ok).0: i32); _3 = ControlFlow::, i32>::Continue(move _11); goto -> bb5; + } + -+ bb9: { ++ bb8: { + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff index f50603a66a56..f04ca72dd6d9 100644 --- a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff @@ -56,7 +56,7 @@ StorageLive(_11); StorageLive(_12); _10 = discriminant(_4); - switchInt(move _10) -> [0: bb8, 1: bb6, otherwise: bb7]; + switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb2]; } bb1: { @@ -114,20 +114,16 @@ _3 = ControlFlow::, i32>::Break(move _13); StorageDead(_13); - goto -> bb5; -+ goto -> bb9; ++ goto -> bb8; } bb7: { - unreachable; - } - - bb8: { _11 = move ((_4 as Ok).0: i32); _3 = ControlFlow::, i32>::Continue(move _11); goto -> bb5; + } + -+ bb9: { ++ bb8: { + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index 66e5c5d3c11c..0cbdaa085bc9 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -50,7 +50,7 @@ fn identity(x: Result) -> Result { // CHECK-LABEL: fn identity( // CHECK: bb0: { // CHECK: [[x:_.*]] = _1; - // CHECK: switchInt(move {{_.*}}) -> [0: bb8, 1: bb6, otherwise: bb7]; + // CHECK: switchInt(move {{_.*}}) -> [0: bb7, 1: bb6, otherwise: bb2]; // CHECK: bb1: { // CHECK: {{_.*}} = (([[controlflow:_.*]] as Continue).0: i32); // CHECK: _0 = Result::::Ok( @@ -68,14 +68,12 @@ fn identity(x: Result) -> Result { // CHECK: bb6: { // CHECK: {{_.*}} = move (([[x]] as Err).0: i32); // CHECK: [[controlflow]] = ControlFlow::, i32>::Break( - // CHECK: goto -> bb9; + // CHECK: goto -> bb8; // CHECK: bb7: { - // CHECK: unreachable; - // CHECK: bb8: { // CHECK: {{_.*}} = move (([[x]] as Ok).0: i32); // CHECK: [[controlflow]] = ControlFlow::, i32>::Continue( // CHECK: goto -> bb5; - // CHECK: bb9: { + // CHECK: bb8: { // CHECK: goto -> bb3; Ok(x?) } diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff index 5cb528c0d5f9..47e0d402347d 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff @@ -56,9 +56,9 @@ + _2 = const Option::::None; StorageLive(_10); - _10 = discriminant(_2); -- switchInt(move _10) -> [0: bb1, 1: bb3, otherwise: bb2]; +- switchInt(move _10) -> [0: bb1, 1: bb2, otherwise: bb6]; + _10 = const 0_isize; -+ switchInt(const 0_isize) -> [0: bb1, 1: bb3, otherwise: bb2]; ++ switchInt(const 0_isize) -> [0: bb1, 1: bb2, otherwise: bb6]; } bb1: { @@ -66,10 +66,6 @@ } bb2: { - unreachable; - } - - bb3: { - _1 = move ((_2 as Some).0: std::alloc::Layout); + _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}; StorageDead(_10); @@ -79,18 +75,18 @@ StorageLive(_5); StorageLive(_6); _9 = const _; -- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb4, unwind unreachable]; -+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb4, unwind unreachable]; +- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb3, unwind unreachable]; ++ _6 = std::alloc::Global::alloc_impl(const {ALLOC1: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb3, unwind unreachable]; } - bb4: { + bb3: { StorageLive(_12); StorageLive(_15); _12 = discriminant(_6); - switchInt(move _12) -> [0: bb6, 1: bb5, otherwise: bb2]; + switchInt(move _12) -> [0: bb5, 1: bb4, otherwise: bb6]; } - bb5: { + bb4: { _15 = const "called `Result::unwrap()` on an `Err` value"; StorageLive(_16); StorageLive(_17); @@ -100,7 +96,7 @@ _14 = result::unwrap_failed(move _15, move _16) -> unwind unreachable; } - bb6: { + bb5: { _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>); StorageDead(_15); StorageDead(_12); @@ -115,6 +111,10 @@ StorageDead(_3); return; } + + bb6: { + unreachable; + } } + + ALLOC0 (size: 8, align: 4) { diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff index 1e1585f20aef..dee57ce6c27b 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff @@ -41,9 +41,9 @@ + _2 = const Option::::None; StorageLive(_10); - _10 = discriminant(_2); -- switchInt(move _10) -> [0: bb2, 1: bb4, otherwise: bb3]; +- switchInt(move _10) -> [0: bb2, 1: bb3, otherwise: bb5]; + _10 = const 0_isize; -+ switchInt(const 0_isize) -> [0: bb2, 1: bb4, otherwise: bb3]; ++ switchInt(const 0_isize) -> [0: bb2, 1: bb3, otherwise: bb5]; } bb1: { @@ -64,10 +64,6 @@ } bb3: { - unreachable; - } - - bb4: { - _1 = move ((_2 as Some).0: std::alloc::Layout); + _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}; StorageDead(_10); @@ -77,12 +73,16 @@ StorageLive(_5); StorageLive(_6); _9 = const _; -- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb5, unwind continue]; -+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb5, unwind continue]; +- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb4, unwind continue]; ++ _6 = std::alloc::Global::alloc_impl(const {ALLOC1: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb4, unwind continue]; + } + + bb4: { + _5 = Result::, std::alloc::AllocError>::unwrap(move _6) -> [return: bb1, unwind continue]; } bb5: { - _5 = Result::, std::alloc::AllocError>::unwrap(move _6) -> [return: bb1, unwind continue]; + unreachable; } } + diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff index e655af559a18..a255b15920cb 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff @@ -56,9 +56,9 @@ + _2 = const Option::::None; StorageLive(_10); - _10 = discriminant(_2); -- switchInt(move _10) -> [0: bb1, 1: bb3, otherwise: bb2]; +- switchInt(move _10) -> [0: bb1, 1: bb2, otherwise: bb6]; + _10 = const 0_isize; -+ switchInt(const 0_isize) -> [0: bb1, 1: bb3, otherwise: bb2]; ++ switchInt(const 0_isize) -> [0: bb1, 1: bb2, otherwise: bb6]; } bb1: { @@ -66,10 +66,6 @@ } bb2: { - unreachable; - } - - bb3: { - _1 = move ((_2 as Some).0: std::alloc::Layout); + _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}; StorageDead(_10); @@ -79,18 +75,18 @@ StorageLive(_5); StorageLive(_6); _9 = const _; -- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb4, unwind unreachable]; -+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}, const false) -> [return: bb4, unwind unreachable]; +- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb3, unwind unreachable]; ++ _6 = std::alloc::Global::alloc_impl(const {ALLOC1: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}, const false) -> [return: bb3, unwind unreachable]; } - bb4: { + bb3: { StorageLive(_12); StorageLive(_15); _12 = discriminant(_6); - switchInt(move _12) -> [0: bb6, 1: bb5, otherwise: bb2]; + switchInt(move _12) -> [0: bb5, 1: bb4, otherwise: bb6]; } - bb5: { + bb4: { _15 = const "called `Result::unwrap()` on an `Err` value"; StorageLive(_16); StorageLive(_17); @@ -100,7 +96,7 @@ _14 = result::unwrap_failed(move _15, move _16) -> unwind unreachable; } - bb6: { + bb5: { _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>); StorageDead(_15); StorageDead(_12); @@ -115,6 +111,10 @@ StorageDead(_3); return; } + + bb6: { + unreachable; + } } + + ALLOC0 (size: 16, align: 8) { diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff index a6658713a026..192ffea25915 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff @@ -41,9 +41,9 @@ + _2 = const Option::::None; StorageLive(_10); - _10 = discriminant(_2); -- switchInt(move _10) -> [0: bb2, 1: bb4, otherwise: bb3]; +- switchInt(move _10) -> [0: bb2, 1: bb3, otherwise: bb5]; + _10 = const 0_isize; -+ switchInt(const 0_isize) -> [0: bb2, 1: bb4, otherwise: bb3]; ++ switchInt(const 0_isize) -> [0: bb2, 1: bb3, otherwise: bb5]; } bb1: { @@ -64,10 +64,6 @@ } bb3: { - unreachable; - } - - bb4: { - _1 = move ((_2 as Some).0: std::alloc::Layout); + _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}; StorageDead(_10); @@ -77,12 +73,16 @@ StorageLive(_5); StorageLive(_6); _9 = const _; -- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb5, unwind continue]; -+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}, const false) -> [return: bb5, unwind continue]; +- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb4, unwind continue]; ++ _6 = std::alloc::Global::alloc_impl(const {ALLOC1: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}, const false) -> [return: bb4, unwind continue]; + } + + bb4: { + _5 = Result::, std::alloc::AllocError>::unwrap(move _6) -> [return: bb1, unwind continue]; } bb5: { - _5 = Result::, std::alloc::AllocError>::unwrap(move _6) -> [return: bb1, unwind continue]; + unreachable; } } + diff --git a/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff b/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff index d287b20c4ead..a12db0a730c5 100644 --- a/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff +++ b/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff @@ -52,7 +52,7 @@ StorageLive(_10); StorageLive(_11); _9 = discriminant(_1); - switchInt(move _9) -> [0: bb6, 1: bb5, otherwise: bb2]; + switchInt(move _9) -> [0: bb5, 1: bb4, otherwise: bb6]; } bb1: { @@ -63,10 +63,6 @@ } bb2: { - unreachable; - } - - bb3: { _6 = ((_3 as Break).0: std::result::Result); _13 = ((_6 as Err).0: i32); _0 = Result::::Err(move _13); @@ -74,27 +70,31 @@ return; } - bb4: { + bb3: { StorageDead(_11); StorageDead(_10); StorageDead(_9); _5 = discriminant(_3); - switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2]; + switchInt(move _5) -> [0: bb1, 1: bb2, otherwise: bb6]; } - bb5: { + bb4: { _11 = ((_1 as Err).0: i32); StorageLive(_12); _12 = Result::::Err(move _11); _3 = ControlFlow::, i32>::Break(move _12); StorageDead(_12); - goto -> bb4; + goto -> bb3; + } + + bb5: { + _10 = ((_1 as Ok).0: i32); + _3 = ControlFlow::, i32>::Continue(move _10); + goto -> bb3; } bb6: { - _10 = ((_1 as Ok).0: i32); - _3 = ControlFlow::, i32>::Continue(move _10); - goto -> bb4; + unreachable; } } diff --git a/tests/mir-opt/separate_const_switch.too_complex.SeparateConstSwitch.diff b/tests/mir-opt/separate_const_switch.too_complex.SeparateConstSwitch.diff index e2bf33f7fbcc..80f40b86919e 100644 --- a/tests/mir-opt/separate_const_switch.too_complex.SeparateConstSwitch.diff +++ b/tests/mir-opt/separate_const_switch.too_complex.SeparateConstSwitch.diff @@ -30,47 +30,47 @@ bb0: { StorageLive(_2); _3 = discriminant(_1); - switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; + switchInt(move _3) -> [0: bb2, 1: bb1, otherwise: bb7]; } bb1: { _6 = ((_1 as Err).0: usize); _2 = ControlFlow::::Break(_6); - goto -> bb4; + goto -> bb3; } bb2: { - unreachable; + _4 = ((_1 as Ok).0: i32); + _2 = ControlFlow::::Continue(_4); + goto -> bb3; } bb3: { - _4 = ((_1 as Ok).0: i32); - _2 = ControlFlow::::Continue(_4); - goto -> bb4; + _8 = discriminant(_2); + switchInt(move _8) -> [0: bb5, 1: bb4, otherwise: bb7]; } bb4: { - _8 = discriminant(_2); - switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb2]; - } - - bb5: { StorageLive(_11); _11 = ((_2 as Break).0: usize); _0 = Option::::None; StorageDead(_11); - goto -> bb7; + goto -> bb6; + } + + bb5: { + _9 = ((_2 as Continue).0: i32); + _0 = Option::::Some(_9); + goto -> bb6; } bb6: { - _9 = ((_2 as Continue).0: i32); - _0 = Option::::Some(_9); - goto -> bb7; - } - - bb7: { StorageDead(_2); return; } + + bb7: { + unreachable; + } } From 8af1a6a1e5a3d673aec7cf67c25401163d344eaf Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 7 Jan 2024 02:41:28 +0000 Subject: [PATCH 156/257] Make ImplTraitPosition display more descriptive --- compiler/rustc_ast_lowering/src/lib.rs | 18 ++++++------- ...ture-gate-impl_trait_in_fn_trait_return.rs | 4 +-- ...-gate-impl_trait_in_fn_trait_return.stderr | 4 +-- tests/ui/impl-trait/where-allowed.stderr | 26 +++++++++---------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index fb59770d48a2..2cd3a54eb363 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -329,24 +329,24 @@ impl std::fmt::Display for ImplTraitPosition { ImplTraitPosition::AsyncBlock => "async blocks", ImplTraitPosition::Bound => "bounds", ImplTraitPosition::Generic => "generics", - ImplTraitPosition::ExternFnParam => "`extern fn` params", - ImplTraitPosition::ClosureParam => "closure params", - ImplTraitPosition::PointerParam => "`fn` pointer params", - ImplTraitPosition::FnTraitParam => "`Fn` trait params", - ImplTraitPosition::TraitParam => "trait method params", - ImplTraitPosition::ImplParam => "`impl` method params", + ImplTraitPosition::ExternFnParam => "`extern fn` parameters", + ImplTraitPosition::ClosureParam => "closure parameters", + ImplTraitPosition::PointerParam => "`fn` pointer parameters", + ImplTraitPosition::FnTraitParam => "the parameters of `Fn` trait bounds", + ImplTraitPosition::TraitParam => "trait method parameters", + ImplTraitPosition::ImplParam => "`impl` method parameters", ImplTraitPosition::ExternFnReturn => "`extern fn` return types", ImplTraitPosition::ClosureReturn => "closure return types", ImplTraitPosition::PointerReturn => "`fn` pointer return types", - ImplTraitPosition::FnTraitReturn => "`Fn` trait return types", + ImplTraitPosition::FnTraitReturn => "the return types of `Fn` trait bounds", ImplTraitPosition::GenericDefault => "generic parameter defaults", ImplTraitPosition::ConstTy => "const types", ImplTraitPosition::StaticTy => "static types", ImplTraitPosition::AssocTy => "associated types", ImplTraitPosition::FieldTy => "field types", - ImplTraitPosition::Cast => "cast types", + ImplTraitPosition::Cast => "cast expression types", ImplTraitPosition::ImplSelf => "impl headers", - ImplTraitPosition::OffsetOf => "`offset_of!` params", + ImplTraitPosition::OffsetOf => "`offset_of!` parameters", }; write!(f, "{name}") diff --git a/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.rs b/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.rs index 1b9530fa82f3..f07abb9d0496 100644 --- a/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.rs +++ b/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.rs @@ -1,6 +1,6 @@ fn f() -> impl Fn() -> impl Sized { || () } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait return +//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds fn g() -> &'static dyn Fn() -> impl Sized { &|| () } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait return +//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.stderr b/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.stderr index f0c0cd040e03..af56e2bd9ef3 100644 --- a/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.stderr +++ b/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.stderr @@ -1,4 +1,4 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait return types +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds --> $DIR/feature-gate-impl_trait_in_fn_trait_return.rs:1:24 | LL | fn f() -> impl Fn() -> impl Sized { || () } @@ -7,7 +7,7 @@ LL | fn f() -> impl Fn() -> impl Sized { || () } = note: see issue #99697 for more information = help: add `#![feature(impl_trait_in_fn_trait_return)]` to the crate attributes to enable -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait return types +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds --> $DIR/feature-gate-impl_trait_in_fn_trait_return.rs:3:32 | LL | fn g() -> &'static dyn Fn() -> impl Sized { &|| () } diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index 2d8895030f2f..9c841342ed38 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -43,7 +43,7 @@ LL | type InReturnInTypeAlias = fn() -> impl Debug; = note: see issue #63063 for more information = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer params +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer parameters --> $DIR/where-allowed.rs:18:40 | LL | fn in_fn_parameter_in_parameters(_: fn(impl Debug)) { panic!() } @@ -55,7 +55,7 @@ error[E0562]: `impl Trait` only allowed in function and inherent method argument LL | fn in_fn_return_in_parameters(_: fn() -> impl Debug) { panic!() } | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer params +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer parameters --> $DIR/where-allowed.rs:26:38 | LL | fn in_fn_parameter_in_return() -> fn(impl Debug) { panic!() } @@ -67,49 +67,49 @@ error[E0562]: `impl Trait` only allowed in function and inherent method argument LL | fn in_fn_return_in_return() -> fn() -> impl Debug { panic!() } | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait params +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:34:49 | LL | fn in_dyn_Fn_parameter_in_parameters(_: &dyn Fn(impl Debug)) { panic!() } | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait return types +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds --> $DIR/where-allowed.rs:38:51 | LL | fn in_dyn_Fn_return_in_parameters(_: &dyn Fn() -> impl Debug) { panic!() } | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait params +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:42:55 | LL | fn in_dyn_Fn_parameter_in_return() -> &'static dyn Fn(impl Debug) { panic!() } | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait params +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:49:51 | LL | fn in_impl_Fn_parameter_in_parameters(_: &impl Fn(impl Debug)) { panic!() } | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait return types +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds --> $DIR/where-allowed.rs:54:53 | LL | fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() } | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait params +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:58:57 | LL | fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic!() } | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait params +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:66:38 | LL | fn in_Fn_parameter_in_generics (_: F) { panic!() } | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait return types +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds --> $DIR/where-allowed.rs:70:40 | LL | fn in_Fn_return_in_generics impl Debug> (_: F) { panic!() } @@ -145,7 +145,7 @@ error[E0562]: `impl Trait` only allowed in function and inherent method argument LL | InTupleVariant(impl Debug), | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `extern fn` params +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `extern fn` parameters --> $DIR/where-allowed.rs:138:33 | LL | fn in_foreign_parameters(_: impl Debug); @@ -205,13 +205,13 @@ error[E0562]: `impl Trait` only allowed in function and inherent method argument LL | where T: PartialEq | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait params +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:205:17 | LL | where T: Fn(impl Debug) | ^^^^^^^^^^ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait return types +error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds --> $DIR/where-allowed.rs:212:22 | LL | where T: Fn() -> impl Debug From 0f3957487b167e0cc6339948244312e0978838d1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 7 Jan 2024 02:48:38 +0000 Subject: [PATCH 157/257] Inline some helpers no longer needed due to RPITIT being stable --- compiler/rustc_ast_lowering/src/lib.rs | 76 ++++++++++---------------- 1 file changed, 29 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 2cd3a54eb363..998a7322fa01 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -304,8 +304,6 @@ enum ImplTraitPosition { ClosureParam, PointerParam, FnTraitParam, - TraitParam, - ImplParam, ExternFnReturn, ClosureReturn, PointerReturn, @@ -333,8 +331,6 @@ impl std::fmt::Display for ImplTraitPosition { ImplTraitPosition::ClosureParam => "closure parameters", ImplTraitPosition::PointerParam => "`fn` pointer parameters", ImplTraitPosition::FnTraitParam => "the parameters of `Fn` trait bounds", - ImplTraitPosition::TraitParam => "trait method parameters", - ImplTraitPosition::ImplParam => "`impl` method parameters", ImplTraitPosition::ExternFnReturn => "`extern fn` return types", ImplTraitPosition::ClosureReturn => "closure return types", ImplTraitPosition::PointerReturn => "`fn` pointer return types", @@ -364,19 +360,6 @@ enum FnDeclKind { Impl, } -impl FnDeclKind { - fn param_impl_trait_allowed(&self) -> bool { - matches!(self, FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait) - } - - fn return_impl_trait_allowed(&self) -> bool { - match self { - FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait => true, - _ => false, - } - } -} - #[derive(Copy, Clone)] enum AstOwner<'a> { NonOwner, @@ -1842,19 +1825,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { inputs = &inputs[..inputs.len() - 1]; } let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| { - let itctx = if kind.param_impl_trait_allowed() { - ImplTraitContext::Universal - } else { - ImplTraitContext::Disallowed(match kind { - FnDeclKind::Fn | FnDeclKind::Inherent => { - unreachable!("fn should allow APIT") - } - FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam, - FnDeclKind::Closure => ImplTraitPosition::ClosureParam, - FnDeclKind::Pointer => ImplTraitPosition::PointerParam, - FnDeclKind::Trait => ImplTraitPosition::TraitParam, - FnDeclKind::Impl => ImplTraitPosition::ImplParam, - }) + let itctx = match kind { + FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait => { + ImplTraitContext::Universal + } + FnDeclKind::ExternFn => { + ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnParam) + } + FnDeclKind::Closure => { + ImplTraitContext::Disallowed(ImplTraitPosition::ClosureParam) + } + FnDeclKind::Pointer => { + ImplTraitContext::Disallowed(ImplTraitPosition::PointerParam) + } }; self.lower_ty_direct(¶m.ty, &itctx) })); @@ -1866,26 +1849,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } None => match &decl.output { FnRetTy::Ty(ty) => { - let context = if kind.return_impl_trait_allowed() { - let fn_def_id = self.local_def_id(fn_node_id); - ImplTraitContext::ReturnPositionOpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), + let itctx = match kind { + FnDeclKind::Fn + | FnDeclKind::Inherent + | FnDeclKind::Trait + | FnDeclKind::Impl => ImplTraitContext::ReturnPositionOpaqueTy { + origin: hir::OpaqueTyOrigin::FnReturn(self.local_def_id(fn_node_id)), fn_kind: kind, + }, + FnDeclKind::ExternFn => { + ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnReturn) + } + FnDeclKind::Closure => { + ImplTraitContext::Disallowed(ImplTraitPosition::ClosureReturn) + } + FnDeclKind::Pointer => { + ImplTraitContext::Disallowed(ImplTraitPosition::PointerReturn) } - } else { - ImplTraitContext::Disallowed(match kind { - FnDeclKind::Fn - | FnDeclKind::Inherent - | FnDeclKind::Trait - | FnDeclKind::Impl => { - unreachable!("fn should allow return-position impl trait in traits") - } - FnDeclKind::ExternFn => ImplTraitPosition::ExternFnReturn, - FnDeclKind::Closure => ImplTraitPosition::ClosureReturn, - FnDeclKind::Pointer => ImplTraitPosition::PointerReturn, - }) }; - hir::FnRetTy::Return(self.lower_ty(ty, &context)) + hir::FnRetTy::Return(self.lower_ty(ty, &itctx)) } FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)), }, From bfd63b20c83512d895b0d6f7c92bd38247b82688 Mon Sep 17 00:00:00 2001 From: Miguel Young de la Sota Date: Mon, 30 Aug 2021 14:18:10 -0400 Subject: [PATCH 158/257] Rewrite `Pin

` docs to clarify guarantees and uses The documentation today does not give a complete treatment of pinning from first principles, which appropriately describes how to design types that use it, nor does it provide formal statements of the guarantees users need to be aware of. This rewrite attempts to address these in a way that makes the concept more approachable while also making the documentation more normative. --- library/core/src/pin.rs | 793 ++++++++++++++++++++++++++++++---------- 1 file changed, 592 insertions(+), 201 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 7d8c881eab80..2632a44a2aeb 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1,107 +1,235 @@ -//! Types that pin data to its location in memory. +//! Types that pin data to a location in memory. //! -//! It is sometimes useful to have objects that are guaranteed not to move, -//! in the sense that their placement in memory does not change, and can thus be relied upon. -//! A prime example of such a scenario would be building self-referential structs, -//! as moving an object with pointers to itself will invalidate them, which could cause undefined -//! behavior. +//! It is sometimes useful to be able to rely upon a certain value not being able to *move*, +//! in the sense that its address in memory cannot change. This is useful specifically when there +//! is a *pointer* that is pointing at that value. The ability to rely on this guarantee that +//! the *value a pointer is pointing at* (its *pointee*) will not move is necessary to implement +//! things like self-referential structs, as moving an object with pointers that are meant to +//! point into that struct's own data would cause those pointers to no longer be valid (they +//! would still be pointing at the struct's old location in memory). //! -//! At a high level, a [Pin]\

ensures that the pointee of any pointer type -//! `P` has a stable location in memory, meaning it cannot be moved elsewhere -//! and its memory cannot be deallocated until it gets dropped. We say that the -//! pointee is "pinned". Things get more subtle when discussing types that -//! combine pinned with non-pinned data; [see below](#projections-and-structural-pinning) -//! for more details. +//! "Pinning" allows us to put a value *which is being pointed at* by some pointer `Ptr` into a state +//! that prevents safe code from *moving* the *pointee* value. In this way, we can allow [`unsafe`] code +//! to rely on the pointer not having its pointee moved out from under it. +//! +//! The rest of this documentation is intended to be the source of truth for users of [`Pin`] +//! in unsafe code; users of [`Pin`] in safe code do not need to read it in detail. //! -//! By default, all types in Rust are movable. Rust allows passing all types by-value, -//! and common smart-pointer types such as [Box]\ and [&mut] T allow -//! replacing and moving the values they contain: you can move out of a [Box]\, -//! or you can use [`mem::swap`]. [Pin]\

wraps a pointer type `P`, so -//! [Pin]<[Box]\> functions much like a regular [Box]\: -//! when a [Pin]<[Box]\> gets dropped, so do its contents, and the memory gets -//! deallocated. Similarly, [Pin]<[&mut] T> is a lot like [&mut] T. -//! However, [Pin]\

does not let clients actually obtain a [Box]\ -//! or [&mut] T to pinned data, which implies that you cannot use operations such -//! as [`mem::swap`]: +//! There are several sections to this documentation: +//! +//! * [What is "*moving*"?][what-is-moving] +//! * [What is "pinning"?][what-is-pinning] +//! * [Examples of types that have address-sensitive states][address-sensitive-examples] +//! * [Self-referential struct][self-ref] +//! * [Intrusive, doubly-linked list][linked-list] +//! * [Subtle Details][subtle-details] +//! +//! # What is "*moving*"? +//! [what-is-moving]: self#what-is-moving +//! +//! When we say a value is *moved*, we mean that the compiler copies, byte-for-byte, the +//! value from one location to another. A move is mechanically identical to [`Copy`]ing +//! a value and discarding the old version of the data, thus making the moved-from value +//! inaccessible. Whenever we write *move* in italics, we mean this precise definition of +//! moving a value. +//! +//! All values in Rust are trivially *moveable*. This means that the address at which a value is +//! located is not necessarily stable in between borrows, and that the compiler is allowed to +//! *move* it to a new address without running any code to notify that value that its address +//! has changed. //! //! ``` -//! use std::pin::Pin; -//! fn swap_pins(x: Pin<&mut T>, y: Pin<&mut T>) { -//! // `mem::swap` needs `&mut T`, but we cannot get it. -//! // We are stuck, we cannot swap the contents of these references. -//! // We could use `Pin::get_unchecked_mut`, but that is unsafe for a reason: -//! // we are not allowed to use it for moving things out of the `Pin`. +//! #[derive(Default)] +//! struct AddrTracker(Option); +//! +//! impl AddrTracker { +//! fn check_for_move(&mut self) { +//! let current_addr = self as *mut Self as usize; +//! match self.0 { +//! None => self.0 = Some(addr), +//! Some(prev_addr) => assert_eq!(prev_addr, current_addr), +//! } +//! } //! } +//! +//! fn take_and_return(tracker: AddrTracker) -> AddrTracker { +//! tracker +//! } +//! +//! let mut tracker = AddrTracker::default(); +//! tracker.check_for_move(); +//! +//! let mut tracker = take_and_return(tracker); +//! +//! // May panic! +//! // tracker.check_for_move(); +//! ``` +//! +//! Although the compiler will not insert moves where no semantic move has occurred, there +//! are many places where a value *may* be moved. For example, when passed into a function. +//! In this sense, Rust does not guarantee that `check_for_move()` will +//! never panic, because the compiler is permitted to *move* `tracker` to enable +//! optimizations like pass-by-value. +//! +//! Common smart-pointer types such as [`Box`] and [`&mut T`] allow removing and replacing the +//! values they contain: you can move out of a [`Box`], or you can use [`mem::swap`]. Putting +//! `tracker` behind a pointer isn't enough on its own to ensure that its address does not change. +//! +//! # What is "pinning"? +//! [what-is-pinning]: #what-is-pinning +//! +//! `Pin` wraps a pointer type `P`, but removes the ability to actually obtain a `P` from it in safe +//! code. By disallowing access to the type behind the pointer, `Pin` prevents us from using +//! operations like [`mem::swap`] to *move* out of the pointer. +//! +//! ## Address-sensitive types +//! +//! One noteworthy example of types which require `Pin` for correctness are the [`Future`]` types +//! generated by the compiler for `async fn`s. +//! +//! For these types, any value which lives "across" an `.await` point is stored in the state +//! machine, including values which borrow from other variables in the `async fn`. *Moving* the +//! [`Future`] in between calls to [`poll`] would invalidate these pointers, leaving the next call +//! to [`poll`] with dangling references! +//! +//! Such types are *address-sensitive*: they incorporate the address of `self` into an +//! operation. These types usually follow a common lifecycle: +//! +//! 1. A value is created which can be freely moved around. +//! * e.g. calling an async function +//! 2. An operation causes the value to depend on its own address using [`unsafe`]. +//! * e.g. calling [`poll`] for the first time on the produced [`Future`] +//! 3. Further [`unsafe`] operations assume that its address is stable. +//! * e.g. subsequent calls to [`poll`] +//! 4. The value is destroyed, undoing its address-sensitivity. +//! * e.g. [`drop`]ping the [`Future`] +//! +//! [`Pin

`] helps us implement steps (2) and (3) safely. +//! +//! ## `Pin` and pointers +//! +//! [`Pin

`] can wrap any pointer type, forming a promise that the pointee will not be *moved*. +//! This promise must be upheld by [`unsafe`] code which interacts with the [`Pin

`] so that +//! [`unsafe`] code can place the pointee in an address-sensitive state that will not be broken +//! by a *move*. Operations on an address-sensitive type accept an argument like +//! [Pin]<[`&mut T`]> or [Pin]<[`Box`]> to indicate this contract to +//! the caller. +//! +//! Since [`Pin

`] can wrap any pointer type, it interacts with +//! [`Deref`] and [`DerefMut`]. A [`Pin

`] where [`P: Deref`][Deref] is a +//! "`P`-style pointer" to a pinned [`P::Target`][Target] – so, a +//! [Pin]<[`Box`]> is an owned pointer to a pinned `T`, and a +//! [Pin]<[`Rc`]> is a reference-counted pointer to a pinned `T`. +//! +//! [`Pin

`] requires that implementations of [`Deref`] and [`DerefMut`] return a pointer to +//! pinned data when they are called on a pinned pointer and do not *move* out of their `self` +//! parameter. It is unsound for [`unsafe`] code to wrap such "evil" pointers; see +//! [`Pin

::new_unchecked`] for details. +//! +//! Pinning does not require any compiler "magic", only a specific contract between the library API +//! and its users. This differs from e.g. [`UnsafeCell`] which changes the semantics of a program's +//! compiled output. A [`Pin

`] is a handle to a value which does not allow moving the value out, +//! but Rust still considers all values themselves to be moveable with e.g. [`mem::swap`]. +//! +//! These guarantees are necessary to make our `AddrTracker` example work. If any code +//! sees a [Pin]<&mut AddrTracker>, it can safely assume that it will *always* see +//! [the same object][address-stability] for the same address (for the lifetime of +//! the pointee). If we had written `check_for_move` above to accept a +//! [Pin]<[`&mut Self`]> instead, multiple calls to it *cannot* panic: +//! +//! ``` +//! # use std::pin::Pin; +//! # #[derive(Default)] +//! # struct AddrTracker(usize); +//! impl AddrTracker { +//! fn check_for_move(self: Pin<&mut Self>) { +//! unsafe { +//! let unpinned = Pin::get_unchecked_mut(self); +//! let addr = unpinned as *mut Self as usize; +//! match unpinned.0 { +//! 0 => unpinned.0 = addr, +//! x => assert_eq!(x, addr), +//! } +//! } +//! } +//! } +//! +//! let mut tracker = Box::pin(AddrTracker::default()); +//! tracker.as_mut().check_for_move(); +//! tracker.as_mut().check_for_move(); //! ``` //! -//! It is worth reiterating that [Pin]\

does *not* change the fact that a Rust -//! compiler considers all types movable. [`mem::swap`] remains callable for any `T`. Instead, -//! [Pin]\

prevents certain *values* (pointed to by pointers wrapped in -//! [Pin]\

) from being moved by making it impossible to call methods that require -//! [&mut] T on them (like [`mem::swap`]). +//! [As discussed below][drop-guarantee], this has consequences for running +//! destructors of pinned memory, too. //! -//! [Pin]\

can be used to wrap any pointer type `P`, and as such it interacts with -//! [`Deref`] and [`DerefMut`]. A [Pin]\

where P: [Deref] should be -//! considered as a "`P`-style pointer" to a pinned P::[Target] – so, a -//! [Pin]<[Box]\> is an owned pointer to a pinned `T`, and a -//! [Pin]<[Rc]\> is a reference-counted pointer to a pinned `T`. -//! For correctness, [Pin]\

relies on the implementations of [`Deref`] and -//! [`DerefMut`] not to move out of their `self` parameter, and only ever to -//! return a pointer to pinned data when they are called on a pinned pointer. +//! ## [`Unpin`] //! -//! # `Unpin` +//! The vast majority of Rust types are not address-sensitive; these types +//! implement the [`Unpin`] auto-trait, which cancels the restrictive effects of +//! [`Pin

`]. When [`T: Unpin`][Unpin], [Pin]<[`Box`]> and +//! [`Box`] function identically, as do [Pin]<[`&mut T`]> and +//! [`&mut T`]. //! -//! Many types are always freely movable, even when pinned, because they do not -//! rely on having a stable address. This includes all the basic types (like -//! [`bool`], [`i32`], and references) as well as types consisting solely of these -//! types. Types that do not care about pinning implement the [`Unpin`] -//! auto-trait, which cancels the effect of [Pin]\

. For T: [Unpin], -//! [Pin]<[Box]\> and [Box]\ function identically, as do -//! [Pin]<[&mut] T> and [&mut] T. +//! This includes all of the basic types, like [`bool`], [`i32`], and [`&T`][&], +//! as well as any other type consisting only of those types. You can opt out of +//! [`Unpin`] via the [`PhantomPinned`] marker type. //! -//! Note that pinning and [`Unpin`] only affect the pointed-to type P::[Target], -//! not the pointer type `P` itself that got wrapped in [Pin]\

. For example, -//! whether or not [Box]\ is [`Unpin`] has no effect on the behavior of -//! [Pin]<[Box]\> (here, `T` is the pointed-to type). +//! Pinning and [`Unpin`] only affect the pointee type [`P::Target`][Target], not the pointer type +//! `P` itself. For example, whether or not [`Box`] is [`Unpin`] has no effect on the behavior of +//! [Pin]<[`Box`]> because `T` is the pointee type. //! -//! # Example: self-referential struct +//! # Examples of address-sensitive types +//! [address-sensitive-examples]: #examples-of-address-sensitive-types //! -//! Before we go into more details to explain the guarantees and choices -//! associated with [Pin]\

, we discuss some examples for how it might be used. -//! Feel free to [skip to where the theoretical discussion continues](#drop-guarantee). +//! ## Self-referential struct +//! [self-ref]: #a-self-referential-struct +//! [`Unmovable`]: #a-self-referential-struct +//! +//! Self-referential structs are the simplest kind of address-sensitive type. +//! +//! It is often useful for a struct to hold a pointer back into itself, which +//! allows the program to efficiently track subsections of the struct. +//! Below, the `slice` field is a pointer into the `data` field, which +//! we could imagine being used to track a sliding window of `data` in parser +//! code. +//! +//! As mentioned before, this pattern is used extensively by compiler-generated +//! [`Future`]s. //! //! ```rust //! use std::pin::Pin; //! use std::marker::PhantomPinned; //! use std::ptr::NonNull; //! -//! // This is a self-referential struct because the slice field points to the data field. -//! // We cannot inform the compiler about that with a normal reference, -//! // as this pattern cannot be described with the usual borrowing rules. -//! // Instead we use a raw pointer, though one which is known not to be null, -//! // as we know it's pointing at the string. +//! /// This is a self-referential struct because `self.slice` points into `self.data`. //! struct Unmovable { -//! data: String, -//! slice: NonNull, +//! /// Backing buffer. +//! data: [u8; 64], +//! /// Points at `self.data` which we know is itself non-null. Raw pointer because we can't do +//! /// this with a normal reference. +//! slice: NonNull<[u8]>, +//! /// Suppress `Unpin` so that this cannot be moved out of a `Pin` once constructed. //! _pin: PhantomPinned, //! } //! //! impl Unmovable { -//! // To ensure the data doesn't move when the function returns, -//! // we place it in the heap where it will stay for the lifetime of the object, -//! // and the only way to access it would be through a pointer to it. -//! fn new(data: String) -> Pin> { +//! /// Create a new `Unmovable`. +//! /// +//! /// To ensure the data doesn't move we place it on the heap behind a pointer which can +//! /// itself be moved. +//! fn new() -> Pin> { //! let res = Unmovable { -//! data, -//! // we only create the pointer once the data is in place -//! // otherwise it will have already moved before we even started -//! slice: NonNull::dangling(), +//! data: [0; 64], +//! // We only create the pointer once the data is in place +//! // otherwise it will have already moved before we even started. +//! slice: NonNull::from(&mut []), //! _pin: PhantomPinned, //! }; //! let mut boxed = Box::pin(res); //! //! let slice = NonNull::from(&boxed.data); -//! // we know this is safe because modifying a field doesn't move the whole struct +//! // We know this is safe, because modifying a field doesn't move the whole +//! // struct. //! unsafe { //! let mut_ref: Pin<&mut Self> = Pin::as_mut(&mut boxed); //! Pin::get_unchecked_mut(mut_ref).slice = slice; @@ -110,79 +238,232 @@ //! } //! } //! -//! let unmoved = Unmovable::new("hello".to_string()); -//! // The pointer should point to the correct location, -//! // so long as the struct hasn't moved. +//! let unmoved = Unmovable::new(); +//! // The pointer should point to the correct location, so long as the struct hasn't moved. //! // Meanwhile, we are free to move the pointer around. //! # #[allow(unused_mut)] //! let mut still_unmoved = unmoved; //! assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data)); //! -//! // Since our type doesn't implement Unpin, this will fail to compile: -//! // let mut new_unmoved = Unmovable::new("world".to_string()); +//! // Since our type doesn't implement `Unpin`, this will fail to compile. +//! // We cannot mutably dereference a `Pin` to `!Unpin` data. +//! // let mut new_unmoved = Unmovable::new(); //! // std::mem::swap(&mut *still_unmoved, &mut *new_unmoved); //! ``` //! -//! # Example: intrusive doubly-linked list +//! ## Intrusive, doubly-linked list +//! [linked-list]: #an-intrusive-doubly-linked-list +//! [`Node`]: #an-intrusive-doubly-linked-list +//! [`List`]: #an-intrusive-doubly-linked-list +//! [`&mut Node`]: #an-intrusive-doubly-linked-list +//! [`List::append`]: #an-intrusive-doubly-linked-list //! -//! In an intrusive doubly-linked list, the collection does not actually allocate -//! the memory for the elements itself. Allocation is controlled by the clients, -//! and elements can live on a stack frame that lives shorter than the collection does. //! -//! To make this work, every element has pointers to its predecessor and successor in -//! the list. Elements can only be added when they are pinned, because moving the elements -//! around would invalidate the pointers. Moreover, the [`Drop`][Drop] implementation of a linked -//! list element will patch the pointers of its predecessor and successor to remove itself -//! from the list. +//! In an intrusive doubly-linked list, the collection does not actually allocate the memory for the +//! nodes itself. Allocation is controlled by the clients, and nodes can live on a stack frame +//! that lives shorter than the collection does provided the nodes are removed from the +//! collection before returning. //! -//! Crucially, we have to be able to rely on [`drop`] being called. If an element -//! could be deallocated or otherwise invalidated without calling [`drop`], the pointers into it -//! from its neighboring elements would become invalid, which would break the data structure. +//! Every node has pointers to its predecessor and successor in the list. Nodes can only be +//! added when they are pinned, because moving the nodes around would invalidate the pointers. +//! The [`Drop`] implementation of a linked list node will patch the pointers of its predecessor +//! and successor to remove itself from the list; not doing so would result in a use-after-free when +//! the list went to refer to the destroyed node. //! -//! Therefore, pinning also comes with a [`drop`]-related guarantee. +//! ```rust +//! use std::marker::PhantomPinned; +//! use std::pin::Pin; +//! use std::ptr; //! -//! # `Drop` guarantee +//! /// The list itself is Unpin, because it doesn't actually hold any data that cannot move +//! /// (although, if nodes held a reference back into it, it would need to be `!Unpin`). +//! /// +//! /// Holds the first and last nodes in the list; they are either both null or both non-null. +//! struct List { +//! start: *mut Node, +//! end: *mut Node, +//! } //! -//! The purpose of pinning is to be able to rely on the placement of some data in memory. -//! To make this work, not just moving the data is restricted; deallocating, repurposing, or -//! otherwise invalidating the memory used to store the data is restricted, too. -//! Concretely, for pinned data you have to maintain the invariant -//! that *its memory will not get invalidated or repurposed from the moment it gets pinned until -//! when [`drop`] is called*. Only once [`drop`] returns or panics, the memory may be reused. +//! impl List { +//! fn new() -> Self { +//! Self { start: ptr::null_mut(), end: ptr::null_mut() } +//! } //! -//! Memory can be "invalidated" by deallocation, but also by -//! replacing a [Some]\(v) by [`None`], or calling [`Vec::set_len`] to "kill" some -//! elements off of a vector. It can be repurposed by using [`ptr::write`] to overwrite it without -//! calling the destructor first. None of this is allowed for pinned data without calling [`drop`]. +//! /// Appends the pinned `node` to the end of the list. +//! /// +//! /// For this function to be correct, we need two guarantees: +//! /// +//! /// 1. `node` never moves again so that our raw pointers to it are always valid +//! /// 2. If a `Node`'s memory would be re-used, its destructor gets run first, removing the +//! /// would-be-dangling references from the list. +//! fn append(&mut self, node: Pin<&mut Node>) { +//! // We could make `List: !Unpin` and track *which* list owns a node if we were fancier. +//! assert!( +//! node.pred.is_null() && node.succ.is_null(), +//! "Node must not already be in another list.", +//! ); //! -//! This is exactly the kind of guarantee that the intrusive linked list from the previous -//! section needs to function correctly. +//! unsafe { +//! // Unpin the `&mut Node`. This is safe, because we're not actually +//! // moving the value, only modifying the pointers inside. This +//! // reference cannot escape this function. +//! let node = Pin::get_unchecked_mut(node); //! -//! Notice that this guarantee does *not* mean that memory does not leak! It is still -//! completely okay to not ever call [`drop`] on a pinned element (e.g., you can still -//! call [`mem::forget`] on a [Pin]<[Box]\>). In the example of the doubly-linked -//! list, that element would just stay in the list. However you must not free or reuse the storage -//! *without calling [`drop`]*. +//! // Rearrange the pointers as appropriate for a doubly-linked list. +//! if self.start.is_null() { +//! self.start = node; +//! } else { +//! (*self.end).succ = node; +//! node.pred = self.end; +//! } +//! self.end = node; +//! } +//! } //! -//! # `Drop` implementation +//! /// Allocates a node on the heap and appends it to the end of the list. +//! fn append_boxed(&mut self) -> Pin> { +//! let mut node = Box::pin(Node { +//! pred: ptr::null_mut(), +//! succ: ptr::null_mut(), +//! data: Data::new(), +//! _pin: PhantomPinned, +//! }); +//! self.append(node.as_mut()); +//! node +//! } +//! } //! -//! If your type uses pinning (such as the two examples above), you have to be careful -//! when implementing [`Drop`][Drop]. The [`drop`] function takes [&mut] self, but this -//! is called *even if your type was previously pinned*! It is as if the -//! compiler automatically called [`Pin::get_unchecked_mut`]. +//! # struct Data; +//! # impl Data { fn new() -> Self { Data } } +//! struct Node { +//! pred: *mut Node, +//! succ: *mut Node, +//! data: Data, +//! /// `Node: Unpin` because `List` expects `Pin`ned pointers to them to remain in place. +//! _pin: PhantomPinned, +//! } //! +<<<<<<< HEAD //! This can never cause a problem in safe code because implementing a type that //! relies on pinning requires unsafe code, but be aware that deciding to make //! use of pinning in your type (for example by implementing some operation on //! [Pin]<[&]Self> or [Pin]<[&mut] Self>) has consequences for your //! [`Drop`][Drop] implementation as well: if an element of your type could have been pinned, //! you must treat [`Drop`][Drop] as implicitly taking [Pin]<[&mut] Self>. +======= +//! impl Drop for Node { +//! /// Remove pointers to `self`, allowing reuse of this memory without clients seeing garbage. +//! fn drop(&mut self) { +//! if self.pred.is_null() || self.succ.is_null() { +//! // Not included: code to remove `self` if it is the head or tail of the list. +//! return; +//! } +>>>>>>> 389e1e2452d (Rewrite `Pin

` docs to clarify guarantees and uses) //! -//! For example, you could implement [`Drop`][Drop] as follows: +//! unsafe { +//! (*self.pred).succ = self.succ; +//! (*self.succ).pred = self.pred; +//! } +//! } +//! } +//! ``` +//! +//! For this to work, a [`drop`-related guarantee][drop-guarantee] is required. If a node could +//! be deallocated or otherwise invalidated without calling [`drop`], the pointers into it from its +//! neighboring elements would become invalid, which would break the data structure. +//! +//! [`List`] itself is *not* address-sensitive. +//! +//! # Subtle details +//! [subtle-details]: #subtle-details +//! +//! [`List::append`] above relies on both of [`Pin

`]'s guarantees: +//! +//! 1. *Address Stability.* If [`unsafe`] code witnesses any [`p: Pin

`][Pin] at any time then +//! it may assume that `p.as_ref().get_ref() as *const _` will remain valid, pointing to the +//! same object until the end of that object's lifetime. +//! 2. *Notice of Destruction.* If `x: T` was ever reachable through any [`Pin

`] type, its +//! destructor must be run (until it either returns or panics) before `x`'s storage can be +//! overwritten. The "until further notice" in (1) includes this mandatory destruction. This is +//! often called the "[`Drop`] guarantee". +//! +//! ## Address Stability +//! [address-stability]: #address-stability +//! +//! The precise meaning of "address stability" is subtle, because "the same object" is not well-defined. +//! It is easiest to reason about it in terms of *visibility of mutations*. If [`unsafe`] code mutates +//! through a [`Pin

`], all code that stashed a raw pointer into it will see the mutation. In other +//! words, [`unsafe`] code can rely on the same value in memory being updated by all uses of a particular +//! [`Pin

`], not to mention that those stashed raw pointers remain valid in the first place. +//! +//! When a [`List`] stores a [`Node`], it needs to assume that appending a second node will mutate the +//! first node, so that later, when the first node is removed, it knows that its predecessor is the +//! second node. +//! +//! When writing generic code, it's not possible to know what [`unsafe`] code has recorded about the +//! pointee's address, so it must be very careful to observe this invariant. Thankfully, most of this +//! is already enforced by the [`Pin

`] API, so only [`unsafe`] code needs to worry about this. +//! +//! ## Notice of Destruction +//! [drop-guarantee]: #notice-of-destruction +//! +//! There needs to be a way for a pinned value to notify any [`unsafe`] code that recorded its address +//! that it is about to be destroyed, so that they can remove its address from their data structures. +//! Thus, in any situation where it would be safe to overwrite a pinned value, the destructor must +//! be called beforehand. +//! +//! The most common storage-reuse situation is when a value on the stack is destroyed as part of a +//! function return, or when heap storage is freed. In both cases, the destructor gets run for us +//! by Rust. However, for heap storage, [`unsafe`] code must make sure to call [`ptr::drop_in_place`] +//! if it wishes to use the [`std::alloc`] APIs manually. +//! +//! However, reuse can happen even if not storage is de-allocated. For example, when a [`Some`] +//! is overwritten by [`None`] using [`ptr::write`], or when [`Vec::set_len`] is used to manually +//! "kill" some elements of a vector. Both of these cases are somewhat contrived, but it is crucial +//! to remember to run destructors of [`Pin`]ned data. As a corollary, the following code can *never* be +//! made safe: +//! +//! ```rust +//! # use std::mem::ManuallyDrop; +//! # use std::pin::Pin; +//! # struct Type; +//! let mut pinned = Box::pin(ManuallyDrop::new(Type)); +//! let inner = unsafe { +//! Pin::map_unchecked_mut(Pin::as_mut(&mut pinned), |x| &mut *x) +//! }; +//! ``` +//! +//! Because [`mem::ManuallyDrop`] inhibits the destructor of `Type`, it won't get run, even though +//! normally [`Box`] drops the `T` before freeing the storage. +//! +//! Of course, *leaking* memory is still fine: [`mem::forget`]ing a [`Box`] +//! prevents its storage from ever getting re-used, so destruction notice does not apply. +//! +//! # Implementing an address-sensitive type. +//! +//! This section goes into detail on important considerations for implementing your own +//! address-sensitive types, which are different from merely using [`Pin

`] in a generic +//! way. +//! +//! ## Implementing [`Drop`] for an `!Unpin` Type +//! [drop-impl]: #implementing-drop-for-an-unpin-type +//! +//! The [`drop`] function takes [`&mut self`], but this is called *even if your +//! type was previously pinned*! Implementing [`Drop`] requires some care, since it is as if +//! the compiler automatically called [`Pin::get_unchecked_mut`]. +//! This can never cause a problem in safe code, because implementing an address-sensitive type +//! requires unsafe code (such as the [linked list above][linked-list]). +//! +//! Beware that deciding to make your type address-sensitive by implementing some operation on +//! [Pin]<[&Self][&]> or [Pin]<[`&mut Self`]> has consequences for your +//! [`Drop`] implementation as well: if an element of your type could have been pinned, +//! you must treat [`Drop`] as implicitly taking [Pin]<[`&mut Self`]>. +//! +//! You should implement [`Drop`] as follows: //! //! ```rust,no_run //! # use std::pin::Pin; -//! # struct Type { } +//! # struct Type; //! impl Drop for Type { //! fn drop(&mut self) { //! // `new_unchecked` is okay because we know this value is never used @@ -195,72 +476,136 @@ //! } //! ``` //! -//! The function `inner_drop` has the type that [`drop`] *should* have, so this makes sure that +//! The function `inner_drop` has the type that [`drop`] *should* have. This makes sure that //! you do not accidentally use `self`/`this` in a way that is in conflict with pinning. //! -//! Moreover, if your type is `#[repr(packed)]`, the compiler will automatically +//! Moreover, if your type is [`#[repr(packed)]`][packed], the compiler will automatically //! move fields around to be able to drop them. It might even do //! that for fields that happen to be sufficiently aligned. As a consequence, you cannot use -//! pinning with a `#[repr(packed)]` type. +//! pinning with a [`#[repr(packed)]`][packed] type. //! -//! # Projections and Structural Pinning +//! ## "Assigning" pinned data //! -//! When working with pinned structs, the question arises how one can access the -//! fields of that struct in a method that takes just [Pin]<[&mut] Struct>. -//! The usual approach is to write helper methods (so called *projections*) -//! that turn [Pin]<[&mut] Struct> into a reference to the field, but what type should -//! that reference have? Is it [Pin]<[&mut] Field> or [&mut] Field? -//! The same question arises with the fields of an `enum`, and also when considering -//! container/wrapper types such as [Vec]\, [Box]\, -//! or [RefCell]\. (This question applies to both mutable and shared references, -//! we just use the more common case of mutable references here for illustration.) +//! Although in general it is not valid to swap data through a [`Pin

`], or assign from +//! a [`Pin

`], for the same reason that a *move* is invalid, there is no particular reason +//! to disallow doing it with specialized functions, as long as they know how to update all +//! uses of the pinned address (and any other `unsafe`-assumed invariants). For [`Unmovable`], +//! we could write //! -//! It turns out that it is actually up to the author of the data structure to decide whether -//! the pinned projection for a particular field turns [Pin]<[&mut] Struct> -//! into [Pin]<[&mut] Field> or [&mut] Field. There are some -//! constraints though, and the most important constraint is *consistency*: -//! every field can be *either* projected to a pinned reference, *or* have -//! pinning removed as part of the projection. If both are done for the same field, -//! that will likely be unsound! +//! ``` +//! # use std::pin::Pin; +//! # use std::marker::PhantomPinned; +//! # use std::ptr::NonNull; +//! # struct Unmovable { +//! # data: [u8; 64], +//! # slice: NonNull<[u8]>, +//! # _pin: PhantomPinned, +//! # } +//! # +//! impl Unmovable { +//! // Copies the contents of `src` into `self`, fixing up the self-pointer +//! // in the process. +//! fn assign(self: Pin<&mut Self>, src: Pin<&mut Self>) { +//! unsafe { +//! let unpinned_self = Pin::into_inner_unchecked(self); +//! let unpinned_src = Pin::into_inner_unchecked(src); +//! *unpinned_self = Self { +//! data: unpinned_src.data, +//! slice: NonNull::from(&mut []), +//! _pin: PhantomPinned, +//! }; //! -//! As the author of a data structure you get to decide for each field whether pinning +//! let data_ptr = unpinned_src.data.as_ptr() as *const u8; +//! let slice_ptr = unpinned_src.slice.as_ptr() as *const u8; +//! let offset = slice_ptr.offset_from(data_ptr) as usize; +//! let len = (*unpinned_src.slice.as_ptr()).len(); +//! +//! unpinned_self.slice = NonNull::from(&mut unpinned_self.data[offset..offset+len]); +//! } +//! } +//! } +//! ``` +//! +//! Even though we can't have the compiler do the assignment for us, it's possible to write +//! such specialized functions for types that might need it. It wouldn't be too difficult +//! implement such a function for the [`Node`], either. +//! +//! Note that it _is_ possible to assign through a [`Pin

`] by way of [`Pin::set()`]. This does +//! not violate any guarantees, since it will run the destructor of the pointee before assigning +//! the new value. +//! +//! ## Projections and Structural Pinning +//! +//! With ordinary structs, it is natural that we want to add *projection* methods +//! that select one of the fields: +//! +//! ``` +//! # struct Field; +//! struct Struct { +//! field: Field, +//! // ... +//! } +//! +//! impl Struct { +//! fn field(&mut self) -> &mut Field { &mut self.field } +//! } +//! ``` +//! +//! When working with address-sensitive types, it's not obvious what the signature of these +//! functions should be. If `field` takes self: [Pin]<[&mut Struct][&mut]>, should it return +//! [`&mut Field`] or [Pin]<[`&mut Field`]>? This question also arises with `enum`s and +//! wrapper types like [`Vec`], [`Box`], and [`RefCell`]. (This question +//! applies just as well to shared references, but we'll examine the more common case +//! of mutable references for illustration). +//! +//! It turns out that it's up to the author of `Struct` to decide which type the projection +//! should produce. The choice must be *consistent* though: each field should only ever +//! be projected as pinned or unpinned; both together will likely be unsound! +//! +//! As the author of a data structure, you get to decide for each field whether pinning //! "propagates" to this field or not. Pinning that propagates is also called "structural", //! because it follows the structure of the type. -//! In the following subsections, we describe the considerations that have to be made -//! for either choice. //! -//! ## Pinning *is not* structural for `field` +//! The choice of whether to pin depends on how the type is being used. If unsafe code +//! that consumes [Pin]\<[&mut Struct][&mut]> also needs to take note of +//! the address of the field itself, it may be evidence that that field is structurally +//! pinned. Unfortunately, there are no hard-and-fast rules. //! -//! It may seem counter-intuitive that the field of a pinned struct might not be pinned, -//! but that is actually the easiest choice: if a [Pin]<[&mut] Field> is never created, -//! nothing can go wrong! So, if you decide that some field does not have structural pinning, -//! all you have to ensure is that you never create a pinned reference to that field. +//! ### When pinning *is not* structural for `field`... +//! +//! While counter-intuitive, it's actually the easier choice: if a [Pin]<[`&mut Field`]> +//! is never created, nothing can go wrong! So, if you decide that some field does not have +//! structural pinning, all you have to ensure is that you never create a pinned reference to that field. //! //! Fields without structural pinning may have a projection method that turns -//! [Pin]<[&mut] Struct> into [&mut] Field: +//! [Pin]<[&mut Struct][&mut]> into [`&mut Field`]: //! //! ```rust,no_run //! # use std::pin::Pin; //! # type Field = i32; //! # struct Struct { field: Field } //! impl Struct { -//! fn pin_get_field(self: Pin<&mut Self>) -> &mut Field { +//! fn field(self: Pin<&mut Self>) -> &mut Field { //! // This is okay because `field` is never considered pinned. //! unsafe { &mut self.get_unchecked_mut().field } //! } //! } //! ``` //! -//! You may also impl [Unpin] for Struct *even if* the type of `field` +//! You may also impl [Unpin] for Struct {} *even if* the type of `field` //! is not [`Unpin`]. What that type thinks about pinning is not relevant -//! when no [Pin]<[&mut] Field> is ever created. +//! when no [Pin]<[`&mut Field`]> is ever created. //! -//! ## Pinning *is* structural for `field` +//! For example, the `data` field of [`Node`] does *not* need +//! to be structurally pinned, because neither [`List`] nor +//! [`Node`] assume anything about it. +//! +//! ### When pinning *is* structural for `field`... //! //! The other option is to decide that pinning is "structural" for `field`, //! meaning that if the struct is pinned then so is the field. //! -//! This allows writing a projection that creates a [Pin]<[&mut] Field>, thus +//! This allows writing a projection that creates a [Pin]<[`&mut Field`]>, thus //! witnessing that the field is pinned: //! //! ```rust,no_run @@ -268,83 +613,98 @@ //! # type Field = i32; //! # struct Struct { field: Field } //! impl Struct { -//! fn pin_get_field(self: Pin<&mut Self>) -> Pin<&mut Field> { +//! fn field(self: Pin<&mut Self>) -> Pin<&mut Field> { //! // This is okay because `field` is pinned when `self` is. //! unsafe { self.map_unchecked_mut(|s| &mut s.field) } //! } //! } //! ``` //! -//! However, structural pinning comes with a few extra requirements: +//! For example, the `prev` and `succ` fields of a [`Node`] +//! are always either null or valid, so [`Node`] could provide a projection with +//! type fn([Pin]<[`&mut Node`]>) -> [Pin]<[`&mut Node`]> for each +//! of them. These fields need to be structurally-pinned, since the outer [`List`] +//! assumes every [`Node`] in it is pinned. //! -//! 1. The struct must only be [`Unpin`] if all the structural fields are -//! [`Unpin`]. This is the default, but [`Unpin`] is a safe trait, so as the author of -//! the struct it is your responsibility *not* to add something like -//! impl\ [Unpin] for Struct\. (Notice that adding a projection operation -//! requires unsafe code, so the fact that [`Unpin`] is a safe trait does not break +//! Structural pinning comes with a few extra requirements: +//! +//! 1. *Structural [`Unpin`].* A struct can be [`Unpin`] if, and only if, all of its +//! structurally-pinned fields are, too. This is [`Unpin`]'s behavior by default. +//! However, as author, it is your responsibility to not write something like +//! unsafe impl\ [Unpin] for Struct\ {}. (Adding *any* projection +//! operation requires unsafe code, so the fact that [`Unpin`] is a safe trait does not break //! the principle that you only have to worry about any of this if you use [`unsafe`].) -//! 2. The destructor of the struct must not move structural fields out of its argument. This -//! is the exact point that was raised in the [previous section][drop-impl]: [`drop`] takes -//! [&mut] self, but the struct (and hence its fields) might have been pinned -//! before. You have to guarantee that you do not move a field inside your [`Drop`][Drop] -//! implementation. In particular, as explained previously, this means that your struct -//! must *not* be `#[repr(packed)]`. -//! See that section for how to write [`drop`] in a way that the compiler can help you -//! not accidentally break pinning. -//! 3. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]: -//! once your struct is pinned, the memory that contains the -//! content is not overwritten or deallocated without calling the content's destructors. -//! This can be tricky, as witnessed by [VecDeque]\: the destructor of -//! [VecDeque]\ can fail to call [`drop`] on all elements if one of the -//! destructors panics. This violates the [`Drop`][Drop] guarantee, because it can lead to -//! elements being deallocated without their destructor being called. -//! ([VecDeque]\ has no pinning projections, so this -//! does not cause unsoundness.) -//! 4. You must not offer any other operations that could lead to data being moved out of +//! +//! 2. *Pinned Destruction.* As discussed [above][drop-impl], [`drop`] takes +//! [`&mut self`], but the struct (and hence its fields) might have been pinned +//! before. The destructor must be written as if its argument was +//! self: [Pin]\<[`&mut Self`]>, instead. +//! +//! As a consequence, the struct *must not* be [`#[repr(packed)]`][packed]. +//! +//! 3. *Structural Notice of Destruction.* You must uphold the the [`Drop` guarantee][drop-guarantee]: +//! once your struct is pinned, the struct's storage cannot be re-used without calling the +//! structurally-pinned fields' destructors, too. +//! +//! This can be tricky, as witnessed by [`VecDeque`]: the destructor of [`VecDeque`] +//! can fail to call [`drop`] on all elements if one of the destructors panics. This violates +//! the [`Drop` guarantee][drop-guarantee], because it can lead to elements being deallocated without +//! their destructor being called. +//! +//! [`VecDeque`] has no pinning projections, so its destructor is sound. If it wanted +//! to provide such structural pinning, its destructor would need to abort the process if any +//! of the destructors panicked. +//! +//! 4. You must not offer any other operations that could lead to data being *moved* out of //! the structural fields when your type is pinned. For example, if the struct contains an -//! [Option]\ and there is a [`take`][Option::take]-like operation with type -//! fn([Pin]<[&mut] Struct\>) -> [Option]\, -//! that operation can be used to move a `T` out of a pinned `Struct` – which means -//! pinning cannot be structural for the field holding this data. +//! [`Option`] and there is a [`take`][Option::take]-like operation with type +//! fn([Pin]<[&mut Struct\][&mut]>) -> [`Option`], +//! then that operation can be used to move a `T` out of a pinned `Struct` – which +//! means pinning cannot be structural for the field holding this data. //! //! For a more complex example of moving data out of a pinned type, -//! imagine if [RefCell]\ had a method -//! fn get_pin_mut(self: [Pin]<[&mut] Self>) -> [Pin]<[&mut] T>. +//! imagine if [`RefCell`] had a method +//! fn get_pin_mut(self: [Pin]<[`&mut Self`]>) -> [Pin]<[`&mut T`]>. //! Then we could do the following: //! ```compile_fail +//! # use std::cell::RefCell; +//! # use std::pin::Pin; //! fn exploit_ref_cell(rc: Pin<&mut RefCell>) { -//! { let p = rc.as_mut().get_pin_mut(); } // Here we get pinned access to the `T`. -//! let rc_shr: &RefCell = rc.into_ref().get_ref(); -//! let b = rc_shr.borrow_mut(); -//! let content = &mut *b; // And here we have `&mut T` to the same data. +//! // Here we get pinned access to the `T`. +//! let _: Pin<&mut T> = rc.as_mut().get_pin_mut(); +//! +//! // And here we have `&mut T` to the same data. +//! let shared: &RefCell = rc.into_ref().get_ref(); +//! let borrow = shared.borrow_mut(); +//! let content = &mut *borrow; //! } //! ``` -//! This is catastrophic, it means we can first pin the content of the -//! [RefCell]\ (using [RefCell]::get_pin_mut) and then move that +//! This is catastrophic: it means we can first pin the content of the +//! [`RefCell`] (using [RefCell]::get_pin_mut) and then move that //! content using the mutable reference we got later. //! -//! ## Examples +//! ### Structural Pinning examples //! -//! For a type like [Vec]\, both possibilities (structural pinning or not) make -//! sense. A [Vec]\ with structural pinning could have `get_pin`/`get_pin_mut` +//! For a type like [`Vec`], both possibilities (structural pinning or not) make +//! sense. A [`Vec`] with structural pinning could have `get_pin`/`get_pin_mut` //! methods to get pinned references to elements. However, it could *not* allow calling -//! [`pop`][Vec::pop] on a pinned [Vec]\ because that would move the (structurally +//! [`pop`][Vec::pop] on a pinned [`Vec`] because that would move the (structurally //! pinned) contents! Nor could it allow [`push`][Vec::push], which might reallocate and thus also //! move the contents. //! -//! A [Vec]\ without structural pinning could -//! impl\ [Unpin] for [Vec]\, because the contents are never pinned -//! and the [Vec]\ itself is fine with being moved as well. +//! A [`Vec`] without structural pinning could +//! impl\ [Unpin] for [`Vec`], because the contents are never pinned +//! and the [`Vec`] itself is fine with being moved as well. //! At that point pinning just has no effect on the vector at all. //! //! In the standard library, pointer types generally do not have structural pinning, -//! and thus they do not offer pinning projections. This is why [Box]\: [Unpin] +//! and thus they do not offer pinning projections. This is why [`Box`]: [Unpin] //! holds for all `T`. It makes sense to do this for pointer types, because moving the -//! [Box]\ does not actually move the `T`: the [Box]\ can be freely -//! movable (aka [`Unpin`]) even if the `T` is not. In fact, even [Pin]<[Box]\> and -//! [Pin]<[&mut] T> are always [`Unpin`] themselves, for the same reason: +//! [`Box`] does not actually move the `T`: the [`Box`] can be freely +//! movable (aka [`Unpin`]) even if the `T` is not. In fact, even [Pin]<[`Box`]> and +//! [Pin]<[`&mut T`]> are always [`Unpin`] themselves, for the same reason: //! their contents (the `T`) are pinned, but the pointers themselves can be moved without moving -//! the pinned data. For both [Box]\ and [Pin]<[Box]\>, +//! the pinned data. For both [`Box`] and [Pin]<[`Box`]>, //! whether the content is pinned is entirely independent of whether the //! pointer is pinned, meaning pinning is *not* structural. //! @@ -352,9 +712,29 @@ //! for the nested futures, as you need to get pinned references to them to call [`poll`]. //! But if your combinator contains any other data that does not need to be pinned, //! you can make those fields not structural and hence freely access them with a -//! mutable reference even when you just have [Pin]<[&mut] Self> (such as in your own -//! [`poll`] implementation). +//! mutable reference even when you just have [Pin]<[`&mut Self`]> +//! (such as in your own [`poll`] implementation). //! +//! [Target]: Deref::Target "ops::Deref::Target" +//! [`drop`]: Drop::drop "ops::Drop::drop" +//! [`poll`]: Future::poll "future::Future::poll" +//! +//! +//! [`std::alloc`]: ../../std/alloc/index.html +//! [`Box`]: ../../std/boxed/struct.Box.html +//! [`Rc`]: ../../std/rc/struct.Rc.html +//! [`Vec`]: ../../std/vec/struct.Vec.html +//! [Vec::pop]: ../../std/vec/struct.Vec.html#method.pop +//! [Vec::push]: ../../std/vec/struct.Vec.html#method.push +//! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len +//! [`VecDeque`]: ../../std/collections/struct.VecDeque.html +//! +//! [`&mut T`]: &mut +//! [`&mut self`]: &mut +//! [`&mut Self`]: &mut +//! [`&mut Field`]: &mut +//! +<<<<<<< HEAD //! [Deref]: crate::ops::Deref "ops::Deref" //! [`Deref`]: crate::ops::Deref "ops::Deref" //! [Target]: crate::ops::Deref::Target "ops::Deref::Target" @@ -377,7 +757,10 @@ //! [`poll`]: crate::future::Future::poll "future::Future::poll" //! [&]: reference "shared reference" //! [&mut]: reference "mutable reference" +======= +>>>>>>> 389e1e2452d (Rewrite `Pin

` docs to clarify guarantees and uses) //! [`unsafe`]: ../../std/keyword.unsafe.html "keyword unsafe" +//! [packed]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked #![stable(feature = "pin", since = "1.33.0")] @@ -386,6 +769,14 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; +#[allow(unused_imports)] +use crate::{ + cell::{RefCell, UnsafeCell}, + future::Future, + marker::PhantomPinned, + mem, ptr, +}; + /// A pinned pointer. /// /// This is a wrapper around a kind of pointer which makes that pointer "pin" its From 8241ca6056bfcdc9aa600c2de6fe8d1fa8baed01 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Mon, 25 Sep 2023 02:08:32 +0200 Subject: [PATCH 159/257] mostly done --- library/core/src/pin.rs | 729 ++++++++++++++++++++-------------------- 1 file changed, 370 insertions(+), 359 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 2632a44a2aeb..a75660522a10 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -2,16 +2,20 @@ //! //! It is sometimes useful to be able to rely upon a certain value not being able to *move*, //! in the sense that its address in memory cannot change. This is useful specifically when there -//! is a *pointer* that is pointing at that value. The ability to rely on this guarantee that -//! the *value a pointer is pointing at* (its *pointee*) will not move is necessary to implement -//! things like self-referential structs, as moving an object with pointers that are meant to -//! point into that struct's own data would cause those pointers to no longer be valid (they -//! would still be pointing at the struct's old location in memory). +//! are one or more *pointers* pointing at that value. The ability to rely on this guarantee that +//! the value a pointer is pointing at (its **pointee**) will +//! +//! 1. Not be *moved* out of its memory location +//! 2. More generally, remain *valid* at that same memory location +//! +//! is necessary to implement things like self-referential structs and intrusive data structures. +//! +//! "Pinning" allows us to put a value *which is being pointed at* by some pointer `Ptr` into a +//! state that prevents safe code from *moving* or otherwise invalidating the *pointee* value at +//! its location in memory (unless the pointee type implements [`Unpin`], which we'll +//! [discuss more below][self#unpin]). In this way, we can allow [`unsafe`] code to rely on the +//! pointer to be valid to dereference. //! -//! "Pinning" allows us to put a value *which is being pointed at* by some pointer `Ptr` into a state -//! that prevents safe code from *moving* the *pointee* value. In this way, we can allow [`unsafe`] code -//! to rely on the pointer not having its pointee moved out from under it. -//! //! The rest of this documentation is intended to be the source of truth for users of [`Pin`] //! in unsafe code; users of [`Pin`] in safe code do not need to read it in detail. //! @@ -19,7 +23,7 @@ //! //! * [What is "*moving*"?][what-is-moving] //! * [What is "pinning"?][what-is-pinning] -//! * [Examples of types that have address-sensitive states][address-sensitive-examples] +//! * [Examples of types with address-sensitive states][address-sensitive-examples] //! * [Self-referential struct][self-ref] //! * [Intrusive, doubly-linked list][linked-list] //! * [Subtle Details][subtle-details] @@ -28,155 +32,262 @@ //! [what-is-moving]: self#what-is-moving //! //! When we say a value is *moved*, we mean that the compiler copies, byte-for-byte, the -//! value from one location to another. A move is mechanically identical to [`Copy`]ing -//! a value and discarding the old version of the data, thus making the moved-from value -//! inaccessible. Whenever we write *move* in italics, we mean this precise definition of -//! moving a value. +//! value from one location to another. In a purely mechanical sense, this is identical to +//! [`Copy`]ing a value from one place in memory to another. In Rust, "move" carries with it the +//! semantics of ownership transfer from one variable to another, which is the key difference +//! between a [`Copy`] and a move. For the purposes of this module's documentation, however, when +//! we write *move* in italics, we mean *specifically* that the value has moved in the mechanical +//! sense of being located at a new place in memory. //! //! All values in Rust are trivially *moveable*. This means that the address at which a value is -//! located is not necessarily stable in between borrows, and that the compiler is allowed to -//! *move* it to a new address without running any code to notify that value that its address -//! has changed. +//! located is not necessarily stable in between borrows. The compiler is allowed to *move* a value +//! to a new address without running any code to notify that value that its address +//! has changed. Although the compiler will not insert memory *moves* where no semantic move has +//! occurred, there are many places where a value *may* be moved. For example, when doing +//! assignment or passing a value into a function. //! //! ``` //! #[derive(Default)] //! struct AddrTracker(Option); //! //! impl AddrTracker { +//! // If we haven't checked the addr of self yet, store the current +//! // address. If we have, confirm that the current address is the same +//! // as it was last time, or else panic. //! fn check_for_move(&mut self) { //! let current_addr = self as *mut Self as usize; //! match self.0 { -//! None => self.0 = Some(addr), +//! None => self.0 = Some(current_addr), //! Some(prev_addr) => assert_eq!(prev_addr, current_addr), //! } //! } //! } -//! -//! fn take_and_return(tracker: AddrTracker) -> AddrTracker { -//! tracker -//! } //! +//! // Create a tracker and store the initial address //! let mut tracker = AddrTracker::default(); //! tracker.check_for_move(); -//! -//! let mut tracker = take_and_return(tracker); -//! +//! +//! // Here we shadow the variable. This carries a semantic move, and may therefore also +//! // come with a mechanical memory *move* +//! let mut tracker = tracker; +//! //! // May panic! //! // tracker.check_for_move(); //! ``` -//! -//! Although the compiler will not insert moves where no semantic move has occurred, there -//! are many places where a value *may* be moved. For example, when passed into a function. -//! In this sense, Rust does not guarantee that `check_for_move()` will -//! never panic, because the compiler is permitted to *move* `tracker` to enable -//! optimizations like pass-by-value. //! -//! Common smart-pointer types such as [`Box`] and [`&mut T`] allow removing and replacing the -//! values they contain: you can move out of a [`Box`], or you can use [`mem::swap`]. Putting -//! `tracker` behind a pointer isn't enough on its own to ensure that its address does not change. +//! In this sense, Rust does not guarantee that `check_for_move()` will never panic, because the +//! compiler is permitted to *move* `tracker` in many situations. +//! +//! Common smart-pointer types such as [`Box`] and [`&mut T`] also allow *moving* the underlying +//! *value* they point at: you can move out of a [`Box`], or you can use [`mem::replace`] to +//! move a `T` out of a [`&mut T`]. Therefore, putting a value (such as `tracker` above) behind a +//! pointer isn't enough on its own to ensure that its address does not change. //! //! # What is "pinning"? -//! [what-is-pinning]: #what-is-pinning +//! [what-is-pinning]: self#what-is-pinning //! -//! `Pin` wraps a pointer type `P`, but removes the ability to actually obtain a `P` from it in safe -//! code. By disallowing access to the type behind the pointer, `Pin` prevents us from using -//! operations like [`mem::swap`] to *move* out of the pointer. +//! We say that a value has been *pinned* when it has been put into a state where it is guaranteed +//! to remain *valid* and *located at the same place in memory* from the time it is pinned until its +//! [`drop`] is called. //! -//! ## Address-sensitive types +//! ## Address-sensitive values, AKA "why we need pinning" +//! [address-sensitive-values]: self#address-sensitive-values //! -//! One noteworthy example of types which require `Pin` for correctness are the [`Future`]` types -//! generated by the compiler for `async fn`s. +//! Most values in Rust are entirely okay with being *moved* around at-will. +//! Types for which it is *always* the case that *any* value of that type can be +//! *moved* at-will should implement [`Unpin`], which we will discuss more [below][self#unpin]. //! -//! For these types, any value which lives "across" an `.await` point is stored in the state -//! machine, including values which borrow from other variables in the `async fn`. *Moving* the -//! [`Future`] in between calls to [`poll`] would invalidate these pointers, leaving the next call -//! to [`poll`] with dangling references! +//! [`Pin`] is specifically targeted at allowing the implementation of *safe interfaces* around +//! types which have some state during which they become "address-sensitive." A value in such an +//! "address-sensitive" state is *not* okay with being *moved* around at-will. Such a value must +//! stay *un-moved* and valid during the address-sensitive portion of its lifetime because some +//! interface is relying on those invariants to be true in order for its implementation to be sound. //! -//! Such types are *address-sensitive*: they incorporate the address of `self` into an -//! operation. These types usually follow a common lifecycle: +//! As a motivating example of a type which may become address-sensitive, consider a type which +//! contains a pointer to another piece of its own data, *i.e.* a "self-referential" type. In order +//! such a type to be implemented soundly, the pointer which points into `self`'s data must be +//! proven valid whenever it is accessed. But if that value is *moved*, the pointer will still +//! point to the old location that the value was located and not into the new location of `self`, +//! thus becoming invalid. A key example of such self-referrential types are the state machines +//! generated by the compiler to implement [`Future`] for `async fn`s. +//! +//! Such types that have an *address-sensitive* state usually follow a lifecycle +//! that looks something like so: //! //! 1. A value is created which can be freely moved around. -//! * e.g. calling an async function -//! 2. An operation causes the value to depend on its own address using [`unsafe`]. +//! * e.g. calling an async function which returns a state machine implementing [`Future`] +//! 2. An operation causes the value to depend on its own address not changing //! * e.g. calling [`poll`] for the first time on the produced [`Future`] -//! 3. Further [`unsafe`] operations assume that its address is stable. +//! 3. Further pieces of the safe interface of the type use internal [`unsafe`] operations which +//! assume that the address of the value is stable //! * e.g. subsequent calls to [`poll`] -//! 4. The value is destroyed, undoing its address-sensitivity. +//! 4. Before the value is invalidated (e.g. deallocated), it is *dropped*, giving it a chance to +//! "unregister"/clear outstanding pointers to itself //! * e.g. [`drop`]ping the [`Future`] //! -//! [`Pin

`] helps us implement steps (2) and (3) safely. +//! There are two possible ways to ensure the invariants required for 2. and 3. above (which +//! apply to any address-sensitive type, not just self-referrential types) do not get broken. //! -//! ## `Pin` and pointers +//! 1. Have the value detect when it is moved and update all the pointers that point to itself +//! 2. Guarantee that the address of the value does not change (and that memory is not re-used +//! for anything else) during the time that the pointers to it are expected to be valid to +//! dereference //! -//! [`Pin

`] can wrap any pointer type, forming a promise that the pointee will not be *moved*. -//! This promise must be upheld by [`unsafe`] code which interacts with the [`Pin

`] so that -//! [`unsafe`] code can place the pointee in an address-sensitive state that will not be broken -//! by a *move*. Operations on an address-sensitive type accept an argument like -//! [Pin]<[`&mut T`]> or [Pin]<[`Box`]> to indicate this contract to +//! Since, as we discussed, Rust can move values without notifying them that they have moved, the +//! first option is ruled out. +//! +//! In order to implement the second option, we must in some way enforce its key invariant, +//! *i.e.* prevent the value from being *moved* or otherwise invalidated. (You may notice this +//! sounds an awful lot like the definition of *pinning* a value). There are two ways by +//! which we might enforce this validity invariant in Rust: +//! +//! 1. Offer a wholly `unsafe` API to interact with the object, thus requiring every caller to +//! uphold the invariant themselves; or, +//! 2. Leverage the type system to encode and enforce this invariant by presenting a restricted +//! API surface to interact with the object +//! +//! The first option is quite obviously undesirable, as the `unsafe`ty of the interface will +//! become viral throughout all code that interacts with the object. +//! +//! [`Pin`] is an implementation of the second option, allowing us to pin a value in place +//! until its [`drop`] runs in a way that we can depend on it staying valid in `unsafe` code. +//! +//! ## Using [`Pin`] to pin values +//! +//! In order to pin a value, we wrap a *pointer to that value* (of some type `Ptr`) in a +//! [`Pin`]. [`Pin`] can wrap any pointer type, forming a promise that the **pointee** +//! will not be *moved* or [otherwise invalidated][self#subtle-details]. +//! +//! It is important to stress that the thing in the [`Pin`] is not the value which we want to pin +//! itself, but rather a pointer to that value! A [`Pin`] does not pin the `Ptr` but rather +//! the pointer's ***pointee** value*. +//! +//! In order to accomplish the goal of pinning the pointee value, [`Pin`] restricts access to +//! the wrapped `Ptr` type in safe code. Specifically, [`Pin`] disallows the ability to access +//! the wrapped pointer in ways that would allow the user to *move* the underlying pointee value or +//! otherwise re-use that memory for something else without using [`unsafe`]. For example, a +//! [`Pin<&mut T>`] makes it impossible to obtain the wrapped [&mut] T safely because +//! through that [&mut] T it would be possible to *move* the underlying value out of +//! the pointer with [`mem::replace`], etc. +//! +//! This promise must be upheld manually by [`unsafe`] code which interacts with the [`Pin`] +//! so that other [`unsafe`] code can rely on the pointee value being *un-moved* and valid. +//! Interfaces that operate on values which are in an address-sensitive state accept an argument +//! like [Pin]<[&mut] T> or [Pin]<[Box]\> to indicate this contract to //! the caller. //! -//! Since [`Pin

`] can wrap any pointer type, it interacts with -//! [`Deref`] and [`DerefMut`]. A [`Pin

`] where [`P: Deref`][Deref] is a -//! "`P`-style pointer" to a pinned [`P::Target`][Target] – so, a -//! [Pin]<[`Box`]> is an owned pointer to a pinned `T`, and a -//! [Pin]<[`Rc`]> is a reference-counted pointer to a pinned `T`. -//! -//! [`Pin

`] requires that implementations of [`Deref`] and [`DerefMut`] return a pointer to -//! pinned data when they are called on a pinned pointer and do not *move* out of their `self` -//! parameter. It is unsound for [`unsafe`] code to wrap such "evil" pointers; see -//! [`Pin

::new_unchecked`] for details. -//! -//! Pinning does not require any compiler "magic", only a specific contract between the library API -//! and its users. This differs from e.g. [`UnsafeCell`] which changes the semantics of a program's -//! compiled output. A [`Pin

`] is a handle to a value which does not allow moving the value out, -//! but Rust still considers all values themselves to be moveable with e.g. [`mem::swap`]. -//! -//! These guarantees are necessary to make our `AddrTracker` example work. If any code -//! sees a [Pin]<&mut AddrTracker>, it can safely assume that it will *always* see -//! [the same object][address-stability] for the same address (for the lifetime of -//! the pointee). If we had written `check_for_move` above to accept a -//! [Pin]<[`&mut Self`]> instead, multiple calls to it *cannot* panic: -//! -//! ``` -//! # use std::pin::Pin; -//! # #[derive(Default)] -//! # struct AddrTracker(usize); -//! impl AddrTracker { -//! fn check_for_move(self: Pin<&mut Self>) { -//! unsafe { -//! let unpinned = Pin::get_unchecked_mut(self); -//! let addr = unpinned as *mut Self as usize; -//! match unpinned.0 { -//! 0 => unpinned.0 = addr, -//! x => assert_eq!(x, addr), -//! } -//! } -//! } -//! } -//! -//! let mut tracker = Box::pin(AddrTracker::default()); -//! tracker.as_mut().check_for_move(); -//! tracker.as_mut().check_for_move(); -//! ``` -//! //! [As discussed below][drop-guarantee], this has consequences for running //! destructors of pinned memory, too. //! +//! ## Interaction between [`Deref`] and [`Pin`] +//! +//! Since [`Pin`] can wrap any pointer type, it uses [`Deref`] and [`DerefMut`] in +//! order to identify the type of the pinned pointee data and provide (restricted) access to it. +//! +//! A [`Pin`] where [`Ptr: Deref`][Deref] is a "`Ptr`-style pointer" to a pinned +//! [`P::Target`][Target] – so, a [Pin]<[Box]\> is an owned pointer to a pinned `T`, +//! and a [Pin]<[Rc]\> is a reference-counted pointer to a pinned `T`. +//! +//! [`Pin`] also uses the [`::Target`][Target] type information to modify the +//! interface it is allowed to provide for interacting with that data (for example, when a +//! [`Pin`]-wrapped pointer points at pinned data which implements [`Unpin`], as +//! [discussed below][self#unpin]). +//! +//! [`Pin`] requires that implementations of [`Deref`] and [`DerefMut`] on `Ptr` return a +//! pointer to the pinned data directly and do not *move* out of the `self` parameter during their +//! implementation of [`DerefMut::deref_mut`]. It is unsound for [`unsafe`] code to wrap pointer +//! types with such "malicious" implementations of [`Deref`]; see [`Pin::new_unchecked`] for +//! details. +//! +//! ## Pinning as a library contract +//! +//! Pinning does not require any compiler "magic"[^noalias], only a specific contract between the +//! library API and its users. This differs from e.g. [`UnsafeCell`] which changes the semantics of +//! a program's compiled output. A [`Pin`] is a handle to a value which does not allow moving +//! the value out, but Rust still considers all values themselves to be moveable with e.g. +//! [`mem::replace`]. +//! +//! [^noalias]: There is a bit of nuance here that is still being decided about what the aliasing +//! semantics of `Pin<&mut T>` should be, but this is true as of today. +//! +//! ## Fixing `AddrTracker` +//! +//! The guarantee of a stable address is necessary to make our `AddrTracker` example work. When +//! `check_for_move` sees a [Pin]<&mut AddrTracker>, it can safely assume that value +//! will exist at that same address until said value goes out of scope, and thus multiple calls +//! to it *cannot* panic. +//! +//! ``` +//! use core::marker::PhantomPinned; +//! use core::pin::Pin; +//! use core::pin::pin; +//! +//! #[derive(Default)] +//! struct AddrTracker { +//! prev_addr: Option, +//! // remove auto-implemented Unpin bound to mark this type as having some +//! // address-sensitive state. This is discussed more below. +//! _pin: PhantomPinned, +//! } +//! +//! impl AddrTracker { +//! fn check_for_move(self: Pin<&mut Self>) { +//! let current_addr = &*self as *const Self as usize; +//! match self.prev_addr { +//! None => { +//! // SAFETY: we do not move out of self +//! let self_data_mut = unsafe { self.get_unchecked_mut() }; +//! self_data_mut.prev_addr = Some(current_addr); +//! }, +//! Some(prev_addr) => assert_eq!(prev_addr, current_addr), +//! } +//! } +//! } +//! +//! // 1. Create the value, not yet in an address-sensitive state +//! let tracker = AddrTracker::default(); +//! +//! // 2. Pin the value by putting it behind a Pin-wrapped pointer, thus putting +//! // it into an address-sensitive state +//! let mut ptr_to_pinned_tracker: Pin<&mut AddrTracker> = pin!(tracker); +//! ptr_to_pinned_tracker.as_mut().check_for_move(); +//! +//! // Trying to access `tracker` or pass `ptr_to_pinned_tracker` to anything +//! // that requires mutable access to a non-pinned version of it will no longer +//! // compile +//! +//! // 3. We can now assume that the tracker value will never be moved, thus +//! // this will never panic! +//! ptr_to_pinned_tracker.as_mut().check_for_move(); +//! ``` +//! +//! Note that this invariant is enforced by simply making it impossible to call code that would +//! perform a move on the pinned value. This is the case since the only way to access that pinned +//! value is through the [`Pin`]-wrapped [`&mut T`], which in turn restricts our access. +//! //! ## [`Unpin`] //! -//! The vast majority of Rust types are not address-sensitive; these types +//! The vast majority of Rust types have no address-sensitive states; these types //! implement the [`Unpin`] auto-trait, which cancels the restrictive effects of -//! [`Pin

`]. When [`T: Unpin`][Unpin], [Pin]<[`Box`]> and -//! [`Box`] function identically, as do [Pin]<[`&mut T`]> and +//! [`Pin

`]. When [`T: Unpin`][Unpin], [Pin]<[Box]\> and +//! [`Box`] function identically, as do [Pin]<[&mut] T> and //! [`&mut T`]. //! -//! This includes all of the basic types, like [`bool`], [`i32`], and [`&T`][&], -//! as well as any other type consisting only of those types. You can opt out of -//! [`Unpin`] via the [`PhantomPinned`] marker type. +//! This includes all of the primitive types, like [`bool`], [`i32`], and [&]T, +//! as well as any other type consisting only of those types. //! -//! Pinning and [`Unpin`] only affect the pointee type [`P::Target`][Target], not the pointer type -//! `P` itself. For example, whether or not [`Box`] is [`Unpin`] has no effect on the behavior of -//! [Pin]<[`Box`]> because `T` is the pointee type. +//! Like other auto-traits, the compiler will automatically determine that a type implements +//! [`Unpin`] if all its fields also implement [`Unpin`]. If you are building a type which is built +//! of only [`Unpin`] types but has an address-sensistive state and thus should not itself +//! implement [`Unpin`], you can opt out of [`Unpin`] via adding a field with the +//! [`PhantomPinned`] marker type, as we did with our latest `AddrTracker` example above. +//! +//! Note that the interaction between a [`Pin`] and [`Unpin`] is through the **pointee type** +//! of the value behind the `Ptr`, [`::Target`][Target]. Whether the `Ptr` type itself +//! implements [`Unpin`] does not affect the behavior of a [`Pin`]. For example, whether or not +//! [`Box`] is [`Unpin`] has no effect on the behavior of [Pin]<[Box]\> because +//! `T` is the type of the pointee value, not [`Box`]. So, whether `T` implements [`Unpin`] is +//! the thing that will affect the behavior of the [Pin]<[Box]\>. //! //! # Examples of address-sensitive types //! [address-sensitive-examples]: #examples-of-address-sensitive-types @@ -215,212 +326,125 @@ //! impl Unmovable { //! /// Create a new `Unmovable`. //! /// -//! /// To ensure the data doesn't move we place it on the heap behind a pointer which can -//! /// itself be moved. +//! /// To ensure the data doesn't move we place it on the heap behind a Pin-wrapped pointer. +//! /// Note that the data is pinned, but the pointer to that data can itself still be moved, +//! /// which is important because it means we can return the pointer from the function. //! fn new() -> Pin> { //! let res = Unmovable { //! data: [0; 64], //! // We only create the pointer once the data is in place //! // otherwise it will have already moved before we even started. -//! slice: NonNull::from(&mut []), +//! slice: NonNull::dangling(), //! _pin: PhantomPinned, //! }; -//! let mut boxed = Box::pin(res); +//! // First we put the data in a box, which will be its final resting place +//! let mut boxed = Box::new(res); //! -//! let slice = NonNull::from(&boxed.data); -//! // We know this is safe, because modifying a field doesn't move the whole -//! // struct. -//! unsafe { -//! let mut_ref: Pin<&mut Self> = Pin::as_mut(&mut boxed); -//! Pin::get_unchecked_mut(mut_ref).slice = slice; -//! } -//! boxed +//! // Then we make the slice field point to the proper part of that boxed data. +//! // From now on we need to make sure we don't move the boxed data. +//! boxed.slice = NonNull::from(&boxed.data); +//! +//! // To do that, we pin the data in place by pointing to it with a Pin-wrapped pointer. +//! // Box::into_pin wraps the existing Box pointer to the data in-place without moving, +//! // so we can safely do this now after inserting the slice pointer above, but we have to +//! // take care that we haven't performed any other semantic moves of `res` in between. +//! let pinned = Box::into_pin(box); +//! +//! // Now we can return the pinned (through a Pin-wrapped box) data +//! pinned //! } //! } //! -//! let unmoved = Unmovable::new(); -//! // The pointer should point to the correct location, so long as the struct hasn't moved. +//! let unmovable: Pin> = Unmovable::new(); +//! +//! // The inner pointee `Unmovable` struct will now never be allowed to move. //! // Meanwhile, we are free to move the pointer around. //! # #[allow(unused_mut)] //! let mut still_unmoved = unmoved; //! assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data)); //! //! // Since our type doesn't implement `Unpin`, this will fail to compile. -//! // We cannot mutably dereference a `Pin` to `!Unpin` data. +//! // We cannot mutably dereference a `Pin` to `!Unpin` data (without using `unsafe`) //! // let mut new_unmoved = Unmovable::new(); //! // std::mem::swap(&mut *still_unmoved, &mut *new_unmoved); //! ``` //! //! ## Intrusive, doubly-linked list //! [linked-list]: #an-intrusive-doubly-linked-list -//! [`Node`]: #an-intrusive-doubly-linked-list -//! [`List`]: #an-intrusive-doubly-linked-list -//! [`&mut Node`]: #an-intrusive-doubly-linked-list -//! [`List::append`]: #an-intrusive-doubly-linked-list -//! //! //! In an intrusive doubly-linked list, the collection does not actually allocate the memory for the //! nodes itself. Allocation is controlled by the clients, and nodes can live on a stack frame //! that lives shorter than the collection does provided the nodes are removed from the //! collection before returning. //! -//! Every node has pointers to its predecessor and successor in the list. Nodes can only be -//! added when they are pinned, because moving the nodes around would invalidate the pointers. -//! The [`Drop`] implementation of a linked list node will patch the pointers of its predecessor -//! and successor to remove itself from the list; not doing so would result in a use-after-free when -//! the list went to refer to the destroyed node. +//! To make this work, every element has pointers to its predecessor and successor in +//! the list. Elements can only be added when they are pinned, because moving the elements +//! around would invalidate the pointers. Moreover, the [`Drop`][Drop] implementation of a linked +//! list element will patch the pointers of its predecessor and successor to remove itself +//! from the list. //! -//! ```rust -//! use std::marker::PhantomPinned; -//! use std::pin::Pin; -//! use std::ptr; +//! Crucially, we have to be able to rely on [`drop`] being called before an element is invalidated. +//! If an element could be deallocated or otherwise invalidated without calling [`drop`], the +//! pointers into it from its neighboring elements would become invalid, which would break the data +//! structure. //! -//! /// The list itself is Unpin, because it doesn't actually hold any data that cannot move -//! /// (although, if nodes held a reference back into it, it would need to be `!Unpin`). -//! /// -//! /// Holds the first and last nodes in the list; they are either both null or both non-null. -//! struct List { -//! start: *mut Node, -//! end: *mut Node, -//! } -//! -//! impl List { -//! fn new() -> Self { -//! Self { start: ptr::null_mut(), end: ptr::null_mut() } -//! } -//! -//! /// Appends the pinned `node` to the end of the list. -//! /// -//! /// For this function to be correct, we need two guarantees: -//! /// -//! /// 1. `node` never moves again so that our raw pointers to it are always valid -//! /// 2. If a `Node`'s memory would be re-used, its destructor gets run first, removing the -//! /// would-be-dangling references from the list. -//! fn append(&mut self, node: Pin<&mut Node>) { -//! // We could make `List: !Unpin` and track *which* list owns a node if we were fancier. -//! assert!( -//! node.pred.is_null() && node.succ.is_null(), -//! "Node must not already be in another list.", -//! ); -//! -//! unsafe { -//! // Unpin the `&mut Node`. This is safe, because we're not actually -//! // moving the value, only modifying the pointers inside. This -//! // reference cannot escape this function. -//! let node = Pin::get_unchecked_mut(node); -//! -//! // Rearrange the pointers as appropriate for a doubly-linked list. -//! if self.start.is_null() { -//! self.start = node; -//! } else { -//! (*self.end).succ = node; -//! node.pred = self.end; -//! } -//! self.end = node; -//! } -//! } -//! -//! /// Allocates a node on the heap and appends it to the end of the list. -//! fn append_boxed(&mut self) -> Pin> { -//! let mut node = Box::pin(Node { -//! pred: ptr::null_mut(), -//! succ: ptr::null_mut(), -//! data: Data::new(), -//! _pin: PhantomPinned, -//! }); -//! self.append(node.as_mut()); -//! node -//! } -//! } -//! -//! # struct Data; -//! # impl Data { fn new() -> Self { Data } } -//! struct Node { -//! pred: *mut Node, -//! succ: *mut Node, -//! data: Data, -//! /// `Node: Unpin` because `List` expects `Pin`ned pointers to them to remain in place. -//! _pin: PhantomPinned, -//! } -//! -<<<<<<< HEAD -//! This can never cause a problem in safe code because implementing a type that -//! relies on pinning requires unsafe code, but be aware that deciding to make -//! use of pinning in your type (for example by implementing some operation on -//! [Pin]<[&]Self> or [Pin]<[&mut] Self>) has consequences for your -//! [`Drop`][Drop] implementation as well: if an element of your type could have been pinned, -//! you must treat [`Drop`][Drop] as implicitly taking [Pin]<[&mut] Self>. -======= -//! impl Drop for Node { -//! /// Remove pointers to `self`, allowing reuse of this memory without clients seeing garbage. -//! fn drop(&mut self) { -//! if self.pred.is_null() || self.succ.is_null() { -//! // Not included: code to remove `self` if it is the head or tail of the list. -//! return; -//! } ->>>>>>> 389e1e2452d (Rewrite `Pin

` docs to clarify guarantees and uses) -//! -//! unsafe { -//! (*self.pred).succ = self.succ; -//! (*self.succ).pred = self.pred; -//! } -//! } -//! } -//! ``` -//! -//! For this to work, a [`drop`-related guarantee][drop-guarantee] is required. If a node could -//! be deallocated or otherwise invalidated without calling [`drop`], the pointers into it from its -//! neighboring elements would become invalid, which would break the data structure. -//! -//! [`List`] itself is *not* address-sensitive. +//! Therefore, we rely on [the `Drop` guarantee][drop-guarantee] which comes with pinning data. //! //! # Subtle details //! [subtle-details]: #subtle-details //! -//! [`List::append`] above relies on both of [`Pin

`]'s guarantees: +//! The purpose of pinning is not *just* to prevent a value from being *moved*, but rather more +//! generally to be able to rely on the pinned value *remaining valid **at a specific place*** in +//! memory. //! -//! 1. *Address Stability.* If [`unsafe`] code witnesses any [`p: Pin

`][Pin] at any time then -//! it may assume that `p.as_ref().get_ref() as *const _` will remain valid, pointing to the -//! same object until the end of that object's lifetime. -//! 2. *Notice of Destruction.* If `x: T` was ever reachable through any [`Pin

`] type, its -//! destructor must be run (until it either returns or panics) before `x`'s storage can be -//! overwritten. The "until further notice" in (1) includes this mandatory destruction. This is -//! often called the "[`Drop`] guarantee". +//! To do so, pinning a value adds an *additional* invariant that must be upheld in order for use +//! of the pinned data to be valid, on top of the ones that must be upheld for a non-pinned value of +//! the same type: //! -//! ## Address Stability -//! [address-stability]: #address-stability +//! From the moment a value is pinned by constructing a [`Pin`]-wrapped pointer to it, that value +//! must *remain, **valid***, at that same address in memory, *until its [`drop`] handler is +//! called.* //! -//! The precise meaning of "address stability" is subtle, because "the same object" is not well-defined. -//! It is easiest to reason about it in terms of *visibility of mutations*. If [`unsafe`] code mutates -//! through a [`Pin

`], all code that stashed a raw pointer into it will see the mutation. In other -//! words, [`unsafe`] code can rely on the same value in memory being updated by all uses of a particular -//! [`Pin

`], not to mention that those stashed raw pointers remain valid in the first place. +//! There is some subtlety to this which we have not yet talked about in detail. The invariant above +//! means that, yes, //! -//! When a [`List`] stores a [`Node`], it needs to assume that appending a second node will mutate the -//! first node, so that later, when the first node is removed, it knows that its predecessor is the -//! second node. +//! 1. The value must not be moved out of its location in memory //! -//! When writing generic code, it's not possible to know what [`unsafe`] code has recorded about the -//! pointee's address, so it must be very careful to observe this invariant. Thankfully, most of this -//! is already enforced by the [`Pin

`] API, so only [`unsafe`] code needs to worry about this. +//! but it also implies that, //! -//! ## Notice of Destruction +//! 2. The memory location that stores the value must not get invalidated or otherwise repurposed +//! during the lifetime of the pinned value until its [`drop`] returns or panics +//! +//! This point is subtle but required for intrusive data structures to be implemented soundly. +//! +//! ## `Drop` guarantee //! [drop-guarantee]: #notice-of-destruction //! -//! There needs to be a way for a pinned value to notify any [`unsafe`] code that recorded its address -//! that it is about to be destroyed, so that they can remove its address from their data structures. -//! Thus, in any situation where it would be safe to overwrite a pinned value, the destructor must -//! be called beforehand. +//! There needs to be a way for a pinned value to notify any code that is relying on its pinned +//! status that it is about to be destroyed, so that such code can remove its address from their +//! data structures or otherwise change their behavior with the knowledge that they can no longer +//! rely on that value existing at the same location. +//! +//! Thus, in any situation where we may want to overwrite a pinned value, that value's [`drop`] must +//! be called beforehand (unless the pinned value implements [`Unpin`], in which case we can ignore +//! all of [`Pin`]'s guarantees, as usual). //! //! The most common storage-reuse situation is when a value on the stack is destroyed as part of a -//! function return, or when heap storage is freed. In both cases, the destructor gets run for us -//! by Rust. However, for heap storage, [`unsafe`] code must make sure to call [`ptr::drop_in_place`] -//! if it wishes to use the [`std::alloc`] APIs manually. +//! function return, or when heap storage is freed. In both cases, [`drop`] gets run for us +//! by Rust when using standard safe code. However, for heap or otherwise custom-allocated storage, +//! [`unsafe`] code must make sure to call [`ptr::drop_in_place`] before deallocating and re-using +//! said storage. //! -//! However, reuse can happen even if not storage is de-allocated. For example, when a [`Some`] -//! is overwritten by [`None`] using [`ptr::write`], or when [`Vec::set_len`] is used to manually -//! "kill" some elements of a vector. Both of these cases are somewhat contrived, but it is crucial -//! to remember to run destructors of [`Pin`]ned data. As a corollary, the following code can *never* be +//! However, reuse can happen even if no storage is (de-)allocated. For example, if we had an +//! [`Option`] which contained a [`Some(v)`] where `v` is a pinned value, then `v` would be +//! invalidated by setting that option to [`None`]. +//! +//! Similarly, if a [`Vec`] was used to store pinned values and [`Vec::set_len`] is used to manually +//! "kill" some elements of a vector, all value "killed" would become invalidated. +//! +//! Both of these cases are somewhat contrived, but it is crucial +//! to remember that [`Pin`]ned data *must* be [`drop`]ped before it is invalidated, as a matter of +//! soundness, not just to prevent memory leaks. As a corollary, the following code can *never* be //! made safe: //! //! ```rust @@ -436,8 +460,9 @@ //! Because [`mem::ManuallyDrop`] inhibits the destructor of `Type`, it won't get run, even though //! normally [`Box`] drops the `T` before freeing the storage. //! -//! Of course, *leaking* memory is still fine: [`mem::forget`]ing a [`Box`] -//! prevents its storage from ever getting re-used, so destruction notice does not apply. +//! Of course, *leaking* memory in such a way that its underlying storage will never get invalidated +//! or re-used is still fine: [`mem::forget`]ing a [`Box`] prevents its storage from ever getting +//! re-used, so the [`drop`] guarantee does not apply. //! //! # Implementing an address-sensitive type. //! @@ -445,19 +470,20 @@ //! address-sensitive types, which are different from merely using [`Pin

`] in a generic //! way. //! -//! ## Implementing [`Drop`] for an `!Unpin` Type -//! [drop-impl]: #implementing-drop-for-an-unpin-type +//! ## Implementing [`Drop`] for types with address-sensitive state +//! [drop-impl]: self#implementing-drop-for-types-with-address-sensitive-states //! -//! The [`drop`] function takes [`&mut self`], but this is called *even if your -//! type was previously pinned*! Implementing [`Drop`] requires some care, since it is as if -//! the compiler automatically called [`Pin::get_unchecked_mut`]. -//! This can never cause a problem in safe code, because implementing an address-sensitive type -//! requires unsafe code (such as the [linked list above][linked-list]). +//! The [`drop`] function takes [`&mut self`], but this is called *even if that `self` has been +//! pinned*! Implementing [`Drop`] for a type with address-sensitive states, because if `self` was +//! indeed in an address-sensitive state before [`drop`] was called, it is as if the compiler +//! automatically called [`Pin::get_unchecked_mut`]. //! -//! Beware that deciding to make your type address-sensitive by implementing some operation on -//! [Pin]<[&Self][&]> or [Pin]<[`&mut Self`]> has consequences for your -//! [`Drop`] implementation as well: if an element of your type could have been pinned, -//! you must treat [`Drop`] as implicitly taking [Pin]<[`&mut Self`]>. +//! This can never cause a problem in safe code because implementing a type which has an +//! address-sensitive state which relies on pinning for validity (for example by +//! implementing some operation on [Pin]<[&]Self> or [Pin]<[&mut] Self> +//! which would be invalid if that `self` was not pinned) has consequences for your +//! [`Drop`][Drop] implementation as well: if an element of your type could have been pinned, +//! you must treat [`Drop`][Drop] as implicitly taking [Pin]<[&mut] Self>. //! //! You should implement [`Drop`] as follows: //! @@ -476,8 +502,9 @@ //! } //! ``` //! -//! The function `inner_drop` has the type that [`drop`] *should* have. This makes sure that -//! you do not accidentally use `self`/`this` in a way that is in conflict with pinning. +//! The function `inner_drop` has the signature that [`drop`] *should* have in this situation. +//! This makes sure that you do not accidentally use `self`/`this` in a way that is in conflict +//! with pinning's invariants. //! //! Moreover, if your type is [`#[repr(packed)]`][packed], the compiler will automatically //! move fields around to be able to drop them. It might even do @@ -486,11 +513,11 @@ //! //! ## "Assigning" pinned data //! -//! Although in general it is not valid to swap data through a [`Pin

`], or assign from -//! a [`Pin

`], for the same reason that a *move* is invalid, there is no particular reason +//! Although in general it is not valid to swap data through a [`Pin`], or assign from +//! a [`Pin`], for the same reason that a *move* is invalid, there is no particular reason //! to disallow doing it with specialized functions, as long as they know how to update all -//! uses of the pinned address (and any other `unsafe`-assumed invariants). For [`Unmovable`], -//! we could write +//! uses of the pinned address (and any other `unsafe`-assumed invariants). For [`Unmovable`] ( +//! from the example above) we could write //! //! ``` //! # use std::pin::Pin; @@ -527,11 +554,10 @@ //! ``` //! //! Even though we can't have the compiler do the assignment for us, it's possible to write -//! such specialized functions for types that might need it. It wouldn't be too difficult -//! implement such a function for the [`Node`], either. +//! such specialized functions for types that might need it. //! //! Note that it _is_ possible to assign through a [`Pin

`] by way of [`Pin::set()`]. This does -//! not violate any guarantees, since it will run the destructor of the pointee before assigning +//! not violate any guarantees, since it will run [`drop`] on the pointee value before assigning //! the new value. //! //! ## Projections and Structural Pinning @@ -571,11 +597,14 @@ //! the address of the field itself, it may be evidence that that field is structurally //! pinned. Unfortunately, there are no hard-and-fast rules. //! -//! ### When pinning *is not* structural for `field`... +//! ### Choosing pinning *not to be* structural for `field`... //! -//! While counter-intuitive, it's actually the easier choice: if a [Pin]<[`&mut Field`]> -//! is never created, nothing can go wrong! So, if you decide that some field does not have -//! structural pinning, all you have to ensure is that you never create a pinned reference to that field. +//! While counter-intuitive, it's actually the easier choice: if a +//! [Pin]<[`&mut Field`]> is never created, nothing can go wrong (well, so long as no +//! unsound `unsafe` code is written which expects the invariants of such a [`Pin`] to be upheld +//! without actually using pinning to guarantee them)! So, if you decide that some field does not +//! have structural pinning, all you have to ensure is that you never create [`Pin`]-wrapped +//! reference to that field. //! //! Fields without structural pinning may have a projection method that turns //! [Pin]<[&mut Struct][&mut]> into [`&mut Field`]: @@ -594,13 +623,9 @@ //! //! You may also impl [Unpin] for Struct {} *even if* the type of `field` //! is not [`Unpin`]. What that type thinks about pinning is not relevant -//! when no [Pin]<[`&mut Field`]> is ever created. +//! when no [Pin]<[&mut] Field> is ever created. //! -//! For example, the `data` field of [`Node`] does *not* need -//! to be structurally pinned, because neither [`List`] nor -//! [`Node`] assume anything about it. -//! -//! ### When pinning *is* structural for `field`... +//! ### Choosing pinning *to be* structural for `field`... //! //! The other option is to decide that pinning is "structural" for `field`, //! meaning that if the struct is pinned then so is the field. @@ -620,17 +645,11 @@ //! } //! ``` //! -//! For example, the `prev` and `succ` fields of a [`Node`] -//! are always either null or valid, so [`Node`] could provide a projection with -//! type fn([Pin]<[`&mut Node`]>) -> [Pin]<[`&mut Node`]> for each -//! of them. These fields need to be structurally-pinned, since the outer [`List`] -//! assumes every [`Node`] in it is pinned. -//! //! Structural pinning comes with a few extra requirements: //! //! 1. *Structural [`Unpin`].* A struct can be [`Unpin`] if, and only if, all of its //! structurally-pinned fields are, too. This is [`Unpin`]'s behavior by default. -//! However, as author, it is your responsibility to not write something like +//! However, as a libray author, it is your responsibility not to write something like //! unsafe impl\ [Unpin] for Struct\ {}. (Adding *any* projection //! operation requires unsafe code, so the fact that [`Unpin`] is a safe trait does not break //! the principle that you only have to worry about any of this if you use [`unsafe`].) @@ -644,7 +663,7 @@ //! //! 3. *Structural Notice of Destruction.* You must uphold the the [`Drop` guarantee][drop-guarantee]: //! once your struct is pinned, the struct's storage cannot be re-used without calling the -//! structurally-pinned fields' destructors, too. +//! structurally-pinned fields' destructors, as well. //! //! This can be tricky, as witnessed by [`VecDeque`]: the destructor of [`VecDeque`] //! can fail to call [`drop`] on all elements if one of the destructors panics. This violates @@ -687,7 +706,7 @@ //! //! For a type like [`Vec`], both possibilities (structural pinning or not) make //! sense. A [`Vec`] with structural pinning could have `get_pin`/`get_pin_mut` -//! methods to get pinned references to elements. However, it could *not* allow calling +//! methods to get [`Pin`]-wrapped references to elements. However, it could *not* allow calling //! [`pop`][Vec::pop] on a pinned [`Vec`] because that would move the (structurally //! pinned) contents! Nor could it allow [`push`][Vec::push], which might reallocate and thus also //! move the contents. @@ -709,47 +728,24 @@ //! pointer is pinned, meaning pinning is *not* structural. //! //! When implementing a [`Future`] combinator, you will usually need structural pinning -//! for the nested futures, as you need to get pinned references to them to call [`poll`]. +//! for the nested futures, as you need to get [`Pin`]-wrapped references to them to call [`poll`]. //! But if your combinator contains any other data that does not need to be pinned, //! you can make those fields not structural and hence freely access them with a //! mutable reference even when you just have [Pin]<[`&mut Self`]> //! (such as in your own [`poll`] implementation). //! -//! [Target]: Deref::Target "ops::Deref::Target" -//! [`drop`]: Drop::drop "ops::Drop::drop" -//! [`poll`]: Future::poll "future::Future::poll" -//! -//! -//! [`std::alloc`]: ../../std/alloc/index.html -//! [`Box`]: ../../std/boxed/struct.Box.html -//! [`Rc`]: ../../std/rc/struct.Rc.html -//! [`Vec`]: ../../std/vec/struct.Vec.html -//! [Vec::pop]: ../../std/vec/struct.Vec.html#method.pop -//! [Vec::push]: ../../std/vec/struct.Vec.html#method.push -//! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len -//! [`VecDeque`]: ../../std/collections/struct.VecDeque.html -//! //! [`&mut T`]: &mut //! [`&mut self`]: &mut //! [`&mut Self`]: &mut //! [`&mut Field`]: &mut -//! -<<<<<<< HEAD //! [Deref]: crate::ops::Deref "ops::Deref" //! [`Deref`]: crate::ops::Deref "ops::Deref" //! [Target]: crate::ops::Deref::Target "ops::Deref::Target" //! [`DerefMut`]: crate::ops::DerefMut "ops::DerefMut" //! [`mem::swap`]: crate::mem::swap "mem::swap" //! [`mem::forget`]: crate::mem::forget "mem::forget" -//! [Vec]: ../../std/vec/struct.Vec.html "Vec" -//! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len "Vec::set_len" -//! [Box]: ../../std/boxed/struct.Box.html "Box" -//! [Vec::pop]: ../../std/vec/struct.Vec.html#method.pop "Vec::pop" -//! [Vec::push]: ../../std/vec/struct.Vec.html#method.push "Vec::push" -//! [Rc]: ../../std/rc/struct.Rc.html "rc::Rc" //! [RefCell]: crate::cell::RefCell "cell::RefCell" //! [`drop`]: Drop::drop -//! [VecDeque]: ../../std/collections/struct.VecDeque.html "collections::VecDeque" //! [`ptr::write`]: crate::ptr::write "ptr::write" //! [`Future`]: crate::future::Future "future::Future" //! [drop-impl]: #drop-implementation @@ -757,10 +753,22 @@ //! [`poll`]: crate::future::Future::poll "future::Future::poll" //! [&]: reference "shared reference" //! [&mut]: reference "mutable reference" -======= ->>>>>>> 389e1e2452d (Rewrite `Pin

` docs to clarify guarantees and uses) //! [`unsafe`]: ../../std/keyword.unsafe.html "keyword unsafe" //! [packed]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked +//! [`std::alloc`]: ../../std/alloc/index.html +//! [`Box`]: ../../std/boxed/struct.Box.html +//! [Box]: ../../std/boxed/struct.Box.html "Box" +//! [`Rc`]: ../../std/rc/struct.Rc.html +//! [Rc]: ../../std/rc/struct.Rc.html "rc::Rc" +//! [`Vec`]: ../../std/vec/struct.Vec.html +//! [Vec]: ../../std/vec/struct.Vec.html "Vec" +//! [`Vec`]: ../../std/vec/struct/Vec.html "Vec" +//! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len "Vec::set_len" +//! [Vec::pop]: ../../std/vec/struct.Vec.html#method.pop "Vec::pop" +//! [Vec::push]: ../../std/vec/struct.Vec.html#method.push "Vec::push" +//! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len +//! [`VecDeque`]: ../../std/collections/struct.VecDeque.html +//! [VecDeque]: ../../std/collections/struct.VecDeque.html "collections::VecDeque" #![stable(feature = "pin", since = "1.33.0")] @@ -777,15 +785,15 @@ use crate::{ mem, ptr, }; -/// A pinned pointer. +/// A pointer to a pinned value. /// -/// This is a wrapper around a kind of pointer which makes that pointer "pin" its -/// value in place, preventing the value referenced by that pointer from being moved -/// unless it implements [`Unpin`]. +/// This is a wrapper around any kind of pointer `P` which makes that pointer "pin" its pointee +/// value in place, thus preventing the value referenced by that pointer from being moved or +/// otherwise invalidated unless it implements [`Unpin`]. /// /// `Pin

` is guaranteed to have the same memory layout and ABI as `P`. /// -/// *See the [`pin` module] documentation for an explanation of pinning.* +/// *See the [`pin` module] documentation for an overview and explanation of pinning.* /// /// [`pin` module]: self // @@ -938,7 +946,7 @@ impl Pin

{ /// By using this method, you are making a promise about the `P::Deref` and /// `P::DerefMut` implementations, if they exist. Most importantly, they /// must not move out of their `self` arguments: `Pin::as_mut` and `Pin::as_ref` - /// will call `DerefMut::deref_mut` and `Deref::deref` *on the pinned pointer* + /// will call `DerefMut::deref_mut` and `Deref::deref` *on the pointer type P* /// and expect these methods to uphold the pinning invariants. /// Moreover, by calling this method you promise that the reference `P` /// dereferences to will not be moved out of again; in particular, it @@ -1048,7 +1056,7 @@ impl Pin

{ Pin { pointer } } - /// Gets a pinned shared reference from this pinned pointer. + /// Gets a shared reference to the pinned value this [`Pin`] points to. /// /// This is a generic method to go from `&Pin>` to `Pin<&T>`. /// It is safe because, as part of the contract of `Pin::new_unchecked`, @@ -1084,7 +1092,7 @@ impl Pin

{ } impl Pin

{ - /// Gets a pinned mutable reference from this pinned pointer. + /// Gets a mutable reference to the pinned value this `Pin

` points to. /// /// This is a generic method to go from `&mut Pin>` to `Pin<&mut T>`. /// It is safe because, as part of the contract of `Pin::new_unchecked`, @@ -1092,7 +1100,8 @@ impl Pin

{ /// "Malicious" implementations of `Pointer::DerefMut` are likewise /// ruled out by the contract of `Pin::new_unchecked`. /// - /// This method is useful when doing multiple calls to functions that consume the pinned type. + /// This method is useful when doing multiple calls to functions that consume the + /// [`Pin`]-wrapped pointer. /// /// # Example /// @@ -1119,10 +1128,12 @@ impl Pin

{ unsafe { Pin::new_unchecked(&mut *self.pointer) } } - /// Assigns a new value to the memory behind the pinned reference. + /// Assigns a new value to the memory location pointed to by the `Pin

`. /// - /// This overwrites pinned data, but that is okay: its destructor gets - /// run before being overwritten, so no pinning guarantee is violated. + /// This overwrites pinned data, but that is okay: the original pinned value's destructor gets + /// run before being overwritten and the new value is also a valid value of the same type, so + /// no pinning invariant is violated. See [the `pin` module documentation][self#subtle-details] + /// for more information on how this upholds the pinning invariants. /// /// # Example /// @@ -1132,7 +1143,7 @@ impl Pin

{ /// let mut val: u8 = 5; /// let mut pinned: Pin<&mut u8> = Pin::new(&mut val); /// println!("{}", pinned); // 5 - /// pinned.as_mut().set(10); + /// pinned.set(10); /// println!("{}", pinned); // 10 /// ``` #[stable(feature = "pin", since = "1.33.0")] @@ -1181,9 +1192,9 @@ impl<'a, T: ?Sized> Pin<&'a T> { /// It may seem like there is an issue here with interior mutability: in fact, /// it *is* possible to move a `T` out of a `&RefCell`. However, this is /// not a problem as long as there does not also exist a `Pin<&T>` pointing - /// to the same data, and `RefCell` does not let you create a pinned reference - /// to its contents. See the discussion on ["pinning projections"] for further - /// details. + /// to the inner `T` inside the `RefCell`, and `RefCell` does not let you get a + /// `Pin<&T>` pointer to its contents. See the discussion on ["pinning projections"] + /// for further details. /// /// Note: `Pin` also implements `Deref` to the target, which can be used /// to access the inner value. However, `Deref` only provides a reference @@ -1282,9 +1293,9 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { } impl Pin<&'static T> { - /// Get a pinned reference from a static reference. + /// Get a `Pin`-wrapped reference from a `&'static` reference. /// - /// This is safe, because `T` is borrowed for the `'static` lifetime, which + /// This is safe because `T` is borrowed immutably for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] @@ -1296,7 +1307,7 @@ impl Pin<&'static T> { } impl<'a, P: DerefMut> Pin<&'a mut Pin

> { - /// Gets a pinned mutable reference from this nested pinned pointer. + /// Gets `Pin<&mut T>` to the underlying pinned value from this nested `Pin`-pointer. /// /// This is a generic method to go from `Pin<&mut Pin>>` to `Pin<&mut T>`. It is /// safe because the existence of a `Pin>` ensures that the pointee, `T`, cannot @@ -1335,9 +1346,9 @@ impl<'a, P: DerefMut> Pin<&'a mut Pin

> { } impl Pin<&'static mut T> { - /// Get a pinned mutable reference from a static mutable reference. + /// Get a `Pin`-wrapped mutable reference from a static mutable reference. /// - /// This is safe, because `T` is borrowed for the `'static` lifetime, which + /// This is safe because `T` is borrowed for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] From ba3b9342cc9f3d4421141e42d3c59200f712b3fc Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Mon, 25 Sep 2023 12:55:57 +0200 Subject: [PATCH 160/257] Fix examples, finish polishing --- library/core/src/pin.rs | 135 ++++++++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 48 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index a75660522a10..80e428033aab 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -158,6 +158,10 @@ //! [`Pin`]. [`Pin`] can wrap any pointer type, forming a promise that the **pointee** //! will not be *moved* or [otherwise invalidated][self#subtle-details]. //! +//! We call such a [`Pin`]-wrapped pointer a **pinning pointer,** (or pinning reference, or pinning +//! `Box`, etc.) because its existince is the thing that is pinning the underlying pointee in place: +//! it is the metaphorical "pin" securing the data in place on the pinboard (in memory). +//! //! It is important to stress that the thing in the [`Pin`] is not the value which we want to pin //! itself, but rather a pointer to that value! A [`Pin`] does not pin the `Ptr` but rather //! the pointer's ***pointee** value*. @@ -190,7 +194,7 @@ //! //! [`Pin`] also uses the [`::Target`][Target] type information to modify the //! interface it is allowed to provide for interacting with that data (for example, when a -//! [`Pin`]-wrapped pointer points at pinned data which implements [`Unpin`], as +//! pinning pointer points at pinned data which implements [`Unpin`], as //! [discussed below][self#unpin]). //! //! [`Pin`] requires that implementations of [`Deref`] and [`DerefMut`] on `Ptr` return a @@ -247,7 +251,7 @@ //! // 1. Create the value, not yet in an address-sensitive state //! let tracker = AddrTracker::default(); //! -//! // 2. Pin the value by putting it behind a Pin-wrapped pointer, thus putting +//! // 2. Pin the value by putting it behind a pinning pointer, thus putting //! // it into an address-sensitive state //! let mut ptr_to_pinned_tracker: Pin<&mut AddrTracker> = pin!(tracker); //! ptr_to_pinned_tracker.as_mut().check_for_move(); @@ -263,7 +267,7 @@ //! //! Note that this invariant is enforced by simply making it impossible to call code that would //! perform a move on the pinned value. This is the case since the only way to access that pinned -//! value is through the [`Pin`]-wrapped [`&mut T`], which in turn restricts our access. +//! value is through the pinning [Pin]<[&mut] T>>, which in turn restricts our access. //! //! ## [`Unpin`] //! @@ -326,15 +330,16 @@ //! impl Unmovable { //! /// Create a new `Unmovable`. //! /// -//! /// To ensure the data doesn't move we place it on the heap behind a Pin-wrapped pointer. -//! /// Note that the data is pinned, but the pointer to that data can itself still be moved, -//! /// which is important because it means we can return the pointer from the function. +//! /// To ensure the data doesn't move we place it on the heap behind a pinning Box. +//! /// Note that the data is pinned, but the which is pinning it can itself still be moved. +//! /// This is important because it means we can return the pointer from the function, which +//! /// is itself a kind of move! //! fn new() -> Pin> { //! let res = Unmovable { //! data: [0; 64], //! // We only create the pointer once the data is in place //! // otherwise it will have already moved before we even started. -//! slice: NonNull::dangling(), +//! slice: NonNull::from(&[]), //! _pin: PhantomPinned, //! }; //! // First we put the data in a box, which will be its final resting place @@ -344,14 +349,16 @@ //! // From now on we need to make sure we don't move the boxed data. //! boxed.slice = NonNull::from(&boxed.data); //! -//! // To do that, we pin the data in place by pointing to it with a Pin-wrapped pointer. -//! // Box::into_pin wraps the existing Box pointer to the data in-place without moving, -//! // so we can safely do this now after inserting the slice pointer above, but we have to -//! // take care that we haven't performed any other semantic moves of `res` in between. -//! let pinned = Box::into_pin(box); +//! // To do that, we pin the data in place by pointing to it with a pinning +//! // (`Pin`-wrapped) pointer. +//! // +//! // `Box::into_pin` makes existing `Box` pin the data in-place without moving it, +//! // so we can safely do this now *after* inserting the slice pointer above, but we have +//! // to take care that we haven't performed any other semantic moves of `res` in between. +//! let pin = Box::into_pin(boxed); //! -//! // Now we can return the pinned (through a Pin-wrapped box) data -//! pinned +//! // Now we can return the pinned (through a pinning Box) data +//! pin //! } //! } //! @@ -360,11 +367,11 @@ //! // The inner pointee `Unmovable` struct will now never be allowed to move. //! // Meanwhile, we are free to move the pointer around. //! # #[allow(unused_mut)] -//! let mut still_unmoved = unmoved; +//! let mut still_unmoved = unmovable; //! assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data)); //! +//! // We cannot mutably dereference a `Pin` unless the pointee is `Unpin` or we use unsafe. //! // Since our type doesn't implement `Unpin`, this will fail to compile. -//! // We cannot mutably dereference a `Pin` to `!Unpin` data (without using `unsafe`) //! // let mut new_unmoved = Unmovable::new(); //! // std::mem::swap(&mut *still_unmoved, &mut *new_unmoved); //! ``` @@ -401,7 +408,7 @@ //! of the pinned data to be valid, on top of the ones that must be upheld for a non-pinned value of //! the same type: //! -//! From the moment a value is pinned by constructing a [`Pin`]-wrapped pointer to it, that value +//! From the moment a value is pinned by constructing a [`Pin`]ning pointer to it, that value //! must *remain, **valid***, at that same address in memory, *until its [`drop`] handler is //! called.* //! @@ -436,8 +443,8 @@ //! said storage. //! //! However, reuse can happen even if no storage is (de-)allocated. For example, if we had an -//! [`Option`] which contained a [`Some(v)`] where `v` is a pinned value, then `v` would be -//! invalidated by setting that option to [`None`]. +//! [`Option`] which contained a `Some(v)` where `v` is a pinned value, then `v` would be +//! invalidated by setting that option to `None`. //! //! Similarly, if a [`Vec`] was used to store pinned values and [`Vec::set_len`] is used to manually //! "kill" some elements of a vector, all value "killed" would become invalidated. @@ -451,18 +458,23 @@ //! # use std::mem::ManuallyDrop; //! # use std::pin::Pin; //! # struct Type; -//! let mut pinned = Box::pin(ManuallyDrop::new(Type)); -//! let inner = unsafe { -//! Pin::map_unchecked_mut(Pin::as_mut(&mut pinned), |x| &mut *x) +//! // Pin something inside a `ManuallyDrop`. This is fine on its own. +//! let mut pin: Pin>> = Box::pin(ManuallyDrop::new(Type)); +//! +//! // However, creating a pinning mutable reference to the type *inside* +//! // the `ManuallyDrop` is not! +//! let inner: Pin<&mut Type> = unsafe { +//! Pin::map_unchecked_mut(pin.as_mut(), |x| &mut **x) //! }; //! ``` //! -//! Because [`mem::ManuallyDrop`] inhibits the destructor of `Type`, it won't get run, even though -//! normally [`Box`] drops the `T` before freeing the storage. +//! Because [`mem::ManuallyDrop`] inhibits the destructor of `Type`, it won't get run when the +//! [Box]<[ManuallyDrop]\> is dropped, thus violating the drop guarantee of the +//! [Pin]<[&mut] Type>>. //! //! Of course, *leaking* memory in such a way that its underlying storage will never get invalidated //! or re-used is still fine: [`mem::forget`]ing a [`Box`] prevents its storage from ever getting -//! re-used, so the [`drop`] guarantee does not apply. +//! re-used, so the [`drop`] guarantee is still satisfied. //! //! # Implementing an address-sensitive type. //! @@ -478,12 +490,12 @@ //! indeed in an address-sensitive state before [`drop`] was called, it is as if the compiler //! automatically called [`Pin::get_unchecked_mut`]. //! -//! This can never cause a problem in safe code because implementing a type which has an -//! address-sensitive state which relies on pinning for validity (for example by -//! implementing some operation on [Pin]<[&]Self> or [Pin]<[&mut] Self> -//! which would be invalid if that `self` was not pinned) has consequences for your +//! This can never cause a problem in purely safe code because creating a pinning pointer to +//! a type which has an address-sensitive (thus does not implement `Unpin`) requires `unsafe`, +//! but it is important to note that choosing to take advantage of pinning-related guarantees +//! to justify validity in the implementation of your type has consequences for that type's //! [`Drop`][Drop] implementation as well: if an element of your type could have been pinned, -//! you must treat [`Drop`][Drop] as implicitly taking [Pin]<[&mut] Self>. +//! you must treat [`Drop`][Drop] as implicitly taking self: [Pin]<[&mut] Self>. //! //! You should implement [`Drop`] as follows: //! @@ -511,6 +523,15 @@ //! that for fields that happen to be sufficiently aligned. As a consequence, you cannot use //! pinning with a [`#[repr(packed)]`][packed] type. //! +//! ### Implementing [`Drop`] for pointer types which will be used as [`Pin`]ning pointers +//! +//! It should further be noted that creating a pinning pointer of some type `Ptr` to some underlying +//! `T` which is not `Unpin` *also* carries with it implications on the way that `Ptr` type must +//! implement [`Drop`] (as well as [`Deref`] and [`DerefMut`])! When implementing a pointer type +//! that may be used as a pinning pointer, you must also take the same care described above not to +//! *move* out of or otherwise invalidate the pointee during [`Drop`], [`Deref`], or [`DerefMut`] +//! implementations. +//! //! ## "Assigning" pinned data //! //! Although in general it is not valid to swap data through a [`Pin`], or assign from @@ -600,14 +621,14 @@ //! ### Choosing pinning *not to be* structural for `field`... //! //! While counter-intuitive, it's actually the easier choice: if a -//! [Pin]<[`&mut Field`]> is never created, nothing can go wrong (well, so long as no +//! [Pin]<[&mut] Field> is never created, nothing can go wrong (well, so long as no //! unsound `unsafe` code is written which expects the invariants of such a [`Pin`] to be upheld //! without actually using pinning to guarantee them)! So, if you decide that some field does not -//! have structural pinning, all you have to ensure is that you never create [`Pin`]-wrapped +//! have structural pinning, all you have to ensure is that you never create pinning //! reference to that field. //! //! Fields without structural pinning may have a projection method that turns -//! [Pin]<[&mut Struct][&mut]> into [`&mut Field`]: +//! [Pin]<[&mut] Struct> into [`&mut Field`]: //! //! ```rust,no_run //! # use std::pin::Pin; @@ -706,7 +727,7 @@ //! //! For a type like [`Vec`], both possibilities (structural pinning or not) make //! sense. A [`Vec`] with structural pinning could have `get_pin`/`get_pin_mut` -//! methods to get [`Pin`]-wrapped references to elements. However, it could *not* allow calling +//! methods to get pinning references to elements. However, it could *not* allow calling //! [`pop`][Vec::pop] on a pinned [`Vec`] because that would move the (structurally //! pinned) contents! Nor could it allow [`push`][Vec::push], which might reallocate and thus also //! move the contents. @@ -728,8 +749,8 @@ //! pointer is pinned, meaning pinning is *not* structural. //! //! When implementing a [`Future`] combinator, you will usually need structural pinning -//! for the nested futures, as you need to get [`Pin`]-wrapped references to them to call [`poll`]. -//! But if your combinator contains any other data that does not need to be pinned, +//! for the nested futures, as you need to get pinning ([`Pin`]-wrapped) references to them to +//! call [`poll`]. But if your combinator contains any other data that does not need to be pinned, //! you can make those fields not structural and hence freely access them with a //! mutable reference even when you just have [Pin]<[`&mut Self`]> //! (such as in your own [`poll`] implementation). @@ -744,6 +765,7 @@ //! [`DerefMut`]: crate::ops::DerefMut "ops::DerefMut" //! [`mem::swap`]: crate::mem::swap "mem::swap" //! [`mem::forget`]: crate::mem::forget "mem::forget" +//! [ManuallyDrop]: crate::mem::ManuallyDrop "ManuallyDrop" //! [RefCell]: crate::cell::RefCell "cell::RefCell" //! [`drop`]: Drop::drop //! [`ptr::write`]: crate::ptr::write "ptr::write" @@ -785,17 +807,32 @@ use crate::{ mem, ptr, }; -/// A pointer to a pinned value. +/// A pointer which pins its pointee in place. /// -/// This is a wrapper around any kind of pointer `P` which makes that pointer "pin" its pointee -/// value in place, thus preventing the value referenced by that pointer from being moved or -/// otherwise invalidated unless it implements [`Unpin`]. +/// [`Pin`] is a wrapper around some kind of pointer `Ptr` which makes that pointer "pin" its +/// pointee value in place, thus preventing the value referenced by that pointer from being moved or +/// otherwise invalidated at that place in memory unless it implements [`Unpin`]. +/// +/// ## Pinning values with [`Pin`] +/// +/// In order to pin a value, we wrap a *pointer to that value* (of some type `Ptr`) in a +/// [`Pin`]. [`Pin`] can wrap any pointer type, forming a promise that the **pointee** +/// will not be *moved* or [otherwise invalidated][self#subtle-details]. +/// +/// We call such a [`Pin`]-wrapped pointer a **pinning pointer,** (or pinning ref, or pinning +/// [`Box`], etc.) because its existince is the thing that is pinning the underlying pointee in +/// place: it is the metaphorical "pin" securing the data in place on the pinboard (in memory). +/// +/// It is important to stress that the thing in the [`Pin`] is not the value which we want to pin +/// itself, but rather a pointer to that value! A [`Pin`] does not pin the `Ptr` but rather +/// the pointer's ***pointee** value*. /// /// `Pin

` is guaranteed to have the same memory layout and ABI as `P`. /// -/// *See the [`pin` module] documentation for an overview and explanation of pinning.* +/// *See the [`pin` module] documentation for a more thorough exploration of pinning.* /// /// [`pin` module]: self +/// [`Box`]: ../../std/boxed/struct.Box.html // // Note: the `Clone` derive below causes unsoundness as it's possible to implement // `Clone` for mutable references. @@ -982,12 +1019,14 @@ impl Pin

{ /// use std::pin::Pin; /// /// fn move_pinned_rc(mut x: Rc) { - /// let pinned = unsafe { Pin::new_unchecked(Rc::clone(&x)) }; + /// // This should mean the pointee can never move again. + /// let pin = unsafe { Pin::new_unchecked(Rc::clone(&x)) }; /// { - /// let p: Pin<&T> = pinned.as_ref(); - /// // This should mean the pointee can never move again. + /// let p: Pin<&T> = pin.as_ref(); + /// // ... /// } - /// drop(pinned); + /// drop(pin); + /// let content = Rc::get_mut(&mut x).unwrap(); // Potential UB down the road ⚠️ /// // Now, if `x` was the only reference, we have a mutable reference to /// // data that we pinned above, which we could use to move it as we have @@ -1101,7 +1140,7 @@ impl Pin

{ /// ruled out by the contract of `Pin::new_unchecked`. /// /// This method is useful when doing multiple calls to functions that consume the - /// [`Pin`]-wrapped pointer. + /// pinning pointer. /// /// # Example /// @@ -1293,7 +1332,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { } impl Pin<&'static T> { - /// Get a `Pin`-wrapped reference from a `&'static` reference. + /// Get a pinning reference from a `&'static` reference. /// /// This is safe because `T` is borrowed immutably for the `'static` lifetime, which /// never ends. @@ -1346,7 +1385,7 @@ impl<'a, P: DerefMut> Pin<&'a mut Pin

> { } impl Pin<&'static mut T> { - /// Get a `Pin`-wrapped mutable reference from a static mutable reference. + /// Get a pinning mutable reference from a static mutable reference. /// /// This is safe because `T` is borrowed for the `'static` lifetime, which /// never ends. From 584f0986fcec6e2cb61d9753a9ccf3ff951d0b3a Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Mon, 25 Sep 2023 13:28:35 +0200 Subject: [PATCH 161/257] fix broken links --- library/core/src/pin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 80e428033aab..146708f45cb6 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -296,7 +296,7 @@ //! # Examples of address-sensitive types //! [address-sensitive-examples]: #examples-of-address-sensitive-types //! -//! ## Self-referential struct +//! ## A self-referential struct //! [self-ref]: #a-self-referential-struct //! [`Unmovable`]: #a-self-referential-struct //! @@ -425,7 +425,7 @@ //! This point is subtle but required for intrusive data structures to be implemented soundly. //! //! ## `Drop` guarantee -//! [drop-guarantee]: #notice-of-destruction +//! [drop-guarantee]: self#drop-guarantee //! //! There needs to be a way for a pinned value to notify any code that is relying on its pinned //! status that it is about to be destroyed, so that such code can remove its address from their From ec8a01a4798ca76b09e4c626fc47eb8cab960e24 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Mon, 25 Sep 2023 13:35:39 +0200 Subject: [PATCH 162/257] fix one more broken link --- library/core/src/pin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 146708f45cb6..dcabf359f52f 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -784,7 +784,7 @@ //! [Rc]: ../../std/rc/struct.Rc.html "rc::Rc" //! [`Vec`]: ../../std/vec/struct.Vec.html //! [Vec]: ../../std/vec/struct.Vec.html "Vec" -//! [`Vec`]: ../../std/vec/struct/Vec.html "Vec" +//! [`Vec`]: ../../std/vec/struct.Vec.html "Vec" //! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len "Vec::set_len" //! [Vec::pop]: ../../std/vec/struct.Vec.html#method.pop "Vec::pop" //! [Vec::push]: ../../std/vec/struct.Vec.html#method.push "Vec::push" From 46f9d77bd1eadd637a78df9e413cb689d39d6246 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Mon, 25 Sep 2023 16:00:35 +0200 Subject: [PATCH 163/257] update doubly linked list commentary and fix links --- library/core/src/pin.rs | 45 ++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index dcabf359f52f..dc4730f4f5d2 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -26,7 +26,7 @@ //! * [Examples of types with address-sensitive states][address-sensitive-examples] //! * [Self-referential struct][self-ref] //! * [Intrusive, doubly-linked list][linked-list] -//! * [Subtle Details][subtle-details] +//! * [Subtle details and the `Drop` guarantee][subtle-details] //! //! # What is "*moving*"? //! [what-is-moving]: self#what-is-moving @@ -156,7 +156,7 @@ //! //! In order to pin a value, we wrap a *pointer to that value* (of some type `Ptr`) in a //! [`Pin`]. [`Pin`] can wrap any pointer type, forming a promise that the **pointee** -//! will not be *moved* or [otherwise invalidated][self#subtle-details]. +//! will not be *moved* or [otherwise invalidated][subtle-details]. //! //! We call such a [`Pin`]-wrapped pointer a **pinning pointer,** (or pinning reference, or pinning //! `Box`, etc.) because its existince is the thing that is pinning the underlying pointee in place: @@ -376,7 +376,7 @@ //! // std::mem::swap(&mut *still_unmoved, &mut *new_unmoved); //! ``` //! -//! ## Intrusive, doubly-linked list +//! ## An intrusive, doubly-linked list //! [linked-list]: #an-intrusive-doubly-linked-list //! //! In an intrusive doubly-linked list, the collection does not actually allocate the memory for the @@ -384,21 +384,26 @@ //! that lives shorter than the collection does provided the nodes are removed from the //! collection before returning. //! -//! To make this work, every element has pointers to its predecessor and successor in -//! the list. Elements can only be added when they are pinned, because moving the elements -//! around would invalidate the pointers. Moreover, the [`Drop`][Drop] implementation of a linked -//! list element will patch the pointers of its predecessor and successor to remove itself -//! from the list. +//! The full implementation details of such a data structure are outside the scope of this +//! documentation, but we will discuss how [`Pin`] can help to do so. //! -//! Crucially, we have to be able to rely on [`drop`] being called before an element is invalidated. -//! If an element could be deallocated or otherwise invalidated without calling [`drop`], the -//! pointers into it from its neighboring elements would become invalid, which would break the data -//! structure. +//! To make such an intrusive data structure work, every element stores pointers to its predecessor +//! and successor within its own data, rather than having the list structure itself manage those +//! pointers. Elements can only be added when they are pinned, because moving the elements +//! around would invalidate the pointers to it which are contained in the element ahead and behind +//! it. Moreover, the [`Drop`][Drop] implementation of the element types themselves will in some +//! way patch the pointers of its predecessor and successor elements to remove itself from the list. //! -//! Therefore, we rely on [the `Drop` guarantee][drop-guarantee] which comes with pinning data. +//! Crucially, this means we have to be able to rely on [`drop`] always being called before that +//! element is invalidated. If an element could be deallocated or otherwise invalidated without +//! calling [`drop`], the pointers into it which are stored in its neighboring elements would +//! become invalid, which would break the data structure. //! -//! # Subtle details -//! [subtle-details]: #subtle-details +//! Therefore, we rely on [the `Drop` guarantee][drop-guarantee] which comes with pinning data, +//! +//! # Subtle details and the `Drop` guarantee +//! [subtle-details]: self#subtle-details-and-the-drop-guarantee +//! [drop-guarantee]: self#subtle-details-and-the-drop-guarantee //! //! The purpose of pinning is not *just* to prevent a value from being *moved*, but rather more //! generally to be able to rely on the pinned value *remaining valid **at a specific place*** in @@ -425,7 +430,6 @@ //! This point is subtle but required for intrusive data structures to be implemented soundly. //! //! ## `Drop` guarantee -//! [drop-guarantee]: self#drop-guarantee //! //! There needs to be a way for a pinned value to notify any code that is relying on its pinned //! status that it is about to be destroyed, so that such code can remove its address from their @@ -482,7 +486,7 @@ //! address-sensitive types, which are different from merely using [`Pin

`] in a generic //! way. //! -//! ## Implementing [`Drop`] for types with address-sensitive state +//! ## Implementing [`Drop`] for types with address-sensitive states //! [drop-impl]: self#implementing-drop-for-types-with-address-sensitive-states //! //! The [`drop`] function takes [`&mut self`], but this is called *even if that `self` has been @@ -817,7 +821,7 @@ use crate::{ /// /// In order to pin a value, we wrap a *pointer to that value* (of some type `Ptr`) in a /// [`Pin`]. [`Pin`] can wrap any pointer type, forming a promise that the **pointee** -/// will not be *moved* or [otherwise invalidated][self#subtle-details]. +/// will not be *moved* or [otherwise invalidated][subtle-details]. /// /// We call such a [`Pin`]-wrapped pointer a **pinning pointer,** (or pinning ref, or pinning /// [`Box`], etc.) because its existince is the thing that is pinning the underlying pointee in @@ -833,6 +837,7 @@ use crate::{ /// /// [`pin` module]: self /// [`Box`]: ../../std/boxed/struct.Box.html +/// [subtle-details]: self#subtle-details-and-the-drop-guarantee // // Note: the `Clone` derive below causes unsoundness as it's possible to implement // `Clone` for mutable references. @@ -1171,7 +1176,7 @@ impl Pin

{ /// /// This overwrites pinned data, but that is okay: the original pinned value's destructor gets /// run before being overwritten and the new value is also a valid value of the same type, so - /// no pinning invariant is violated. See [the `pin` module documentation][self#subtle-details] + /// no pinning invariant is violated. See [the `pin` module documentation][subtle-details] /// for more information on how this upholds the pinning invariants. /// /// # Example @@ -1185,6 +1190,8 @@ impl Pin

{ /// pinned.set(10); /// println!("{}", pinned); // 10 /// ``` + /// + /// [subtle-details]: self#subtle-details-and-the-drop-guarantee #[stable(feature = "pin", since = "1.33.0")] #[inline(always)] pub fn set(&mut self, value: P::Target) From db5b19e4726279d03aa91469a0e7df9de09b47a8 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 26 Sep 2023 18:14:18 +0200 Subject: [PATCH 164/257] improve intro and `Unpin`-related discussion --- library/core/src/marker.rs | 50 +++++++----- library/core/src/pin.rs | 156 ++++++++++++++++++++++++++----------- 2 files changed, 142 insertions(+), 64 deletions(-) diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 69d54f064078..bd19d3cc0e29 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -899,25 +899,28 @@ marker_impls! { {T: ?Sized} &mut T, } -/// Types that can be safely moved after being pinned. +/// Types that do not need to follow the rules of pinning. /// -/// Rust itself has no notion of immovable types, and considers moves (e.g., -/// through assignment or [`mem::replace`]) to always be safe. +/// For information on what "pinning" is, see the [`pin` module] documentation. /// -/// The [`Pin`][Pin] type is used instead to prevent moves through the type -/// system. Pointers `P` wrapped in the [`Pin>`][Pin] wrapper can't be -/// moved out of. See the [`pin` module] documentation for more information on -/// pinning. +/// Implementing the `Unpin` trait for `T` lifts the restrictions of pinning off that type. +/// This means that, if `T: Unpin`, it cannot be assumed that a value of type `T` will be bound +/// by the invariants that pinning infers, *even* when "pinned" by a [`Pin`] pointing at it. +/// When a value of type `T` is pointed at by a [`Pin`], [`Pin`] will not restrict access +/// to the pointee value like it normally would, thus allowing the user to do anything that they +/// normally could with a non-[`Pin`]-wrapped `Ptr` to that value. /// -/// Implementing the `Unpin` trait for `T` lifts the restrictions of pinning off -/// the type, which then allows moving `T` out of [`Pin>`][Pin] with -/// functions such as [`mem::replace`]. +/// For more discussion on the consequences of [`Unpin`] within the wider scope of the pinning +/// system, see [the section about `Unpin`] in the [`pin` module]. /// -/// `Unpin` has no consequence at all for non-pinned data. In particular, -/// [`mem::replace`] happily moves `!Unpin` data (it works for any `&mut T`, not -/// just when `T: Unpin`). However, you cannot use [`mem::replace`] on data -/// wrapped inside a [`Pin>`][Pin] because you cannot get the `&mut T` you -/// need for that, and *that* is what makes this system work. +/// `Unpin` has no consequence at all for non-pinned data. In particular, [`mem::replace`] happily +/// moves `!Unpin` data, which would be immovable when pinned ([`mem::replace`] works for any +/// `&mut T`, not just when `T: Unpin`). +/// +/// *However*, you cannot use [`mem::replace`] on `!Unpin` data which is *pinned* by being wrapped +/// inside a [`Pin`] pointing at it. This is because you cannot (safely) use a +/// [`Pin`] to get an `&mut T` to its pointee value, which you would need to call +/// [`mem::replace`], and *that* is what makes this system work. /// /// So this, for example, can only be done on types implementing `Unpin`: /// @@ -935,11 +938,20 @@ marker_impls! { /// mem::replace(&mut *pinned_string, "other".to_string()); /// ``` /// -/// This trait is automatically implemented for almost every type. +/// This trait is automatically implemented for almost every type. The compiler (and you!) is free +/// to take the conservative stance of marking types as [`Unpin`] by default. This is because if a +/// type implements [`Unpin`], then it is unsound for [`unsafe`] code to assume that type is truly +/// pinned, *even* when viewed through a "pinning" pointer! It is the responsibility of the +/// implementor of [`unsafe`] code that relies upon pinning for soundness to ensure that all the +/// types it expects to be truly pinned do not implement [`Unpin`]. For more details, see the +/// [`pin` module] docs! /// -/// [`mem::replace`]: crate::mem::replace -/// [Pin]: crate::pin::Pin -/// [`pin` module]: crate::pin +/// [`mem::replace`]: crate::mem::replace "mem replace" +/// [`Pin`]: crate::pin::Pin "Pin" +/// [`Pin`]: crate::pin::Pin "Pin" +/// [`pin` module]: crate::pin "pin module" +/// [section about `Unpin`]: crate::pin#unpin "pin module docs about unpin" +/// [`unsafe`]: ../../std/keyword.unsafe.html "keyword unsafe" #[stable(feature = "pin", since = "1.33.0")] #[diagnostic::on_unimplemented( note = "consider using the `pin!` macro\nconsider using `Box::pin` if you need to access the pinned value outside of the current scope", diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index dc4730f4f5d2..485ec689e343 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1,28 +1,53 @@ //! Types that pin data to a location in memory. //! //! It is sometimes useful to be able to rely upon a certain value not being able to *move*, -//! in the sense that its address in memory cannot change. This is useful specifically when there -//! are one or more *pointers* pointing at that value. The ability to rely on this guarantee that -//! the value a pointer is pointing at (its **pointee**) will +//! in the sense that its address in memory cannot change. This is useful especially when there +//! are one or more [*pointers*][pointer] pointing at that value. The ability to rely on this +//! guarantee that the value a [pointer] is pointing at (its **pointee**) will //! //! 1. Not be *moved* out of its memory location //! 2. More generally, remain *valid* at that same memory location //! -//! is necessary to implement things like self-referential structs and intrusive data structures. +//! is called "pinning." We would say that a value which satisfies these guarantees has been +//! "pinned," in that it has been permanently (until the end of its lifetime) attached to its +//! location in memory. Pinning a value is incredibly useful in that it provides the necessary +//! guarantees[^guarantees] for [`unsafe`] code to be able to dereference raw pointers to the pinned +//! value for the duration it is pinned (which, [as we'll see later][drop-guarantee], is necessarily +//! from the time the value is first pinned until the end of its lifetime). This concept of +//! "pinning" is necessary to implement safe interfaces on top of things like self-referential types +//! and intrusive data structures which cannot currently be modeled in fully safe Rust using only +//! borrow-checked [references][reference]. //! -//! "Pinning" allows us to put a value *which is being pointed at* by some pointer `Ptr` into a -//! state that prevents safe code from *moving* or otherwise invalidating the *pointee* value at -//! its location in memory (unless the pointee type implements [`Unpin`], which we'll -//! [discuss more below][self#unpin]). In this way, we can allow [`unsafe`] code to rely on the -//! pointer to be valid to dereference. +//! "Pinning" allows us to put a *value* which exists at some location in memory into a state where +//! safe code cannot *move* that value to a different location in memory or otherwise invalidate it +//! at its current location (unless it implements [`Unpin`], which we will +//! [talk about below][unpin]). Anything that wants to interact with the pinned value in a way that +//! has the potential to violate these guarantees must promise that it will not actually violate +//! them, using the [`unsafe`] keyword to mark that such a promise is upheld by the user and not +//! the compiler. In this way, we can allow other [`unsafe`] code to rely on any pointers that +//! point to the pinned value to be valid to dereference while it is pinned. +//! +//! [^guarantees]: Pinning on its own does not provide *all* the invariants necessary here. However, +//! in order to validly pin a value in the first place, it must already satisfy the other invariants +//! for it to be valid to dereference a pointer to that value while it is pinned, and using the +//! [`Drop` guarantee][drop-guarantee], we can ensure that any interested parties are notified +//! before the value becomes no longer pinned, i.e. when the value goes out of scope and is +//! invalidated. +//! +//! Note that as long as you don't use [`unsafe`], it's impossible to create or misuse a pinned +//! value in a way that will produce unsoundness. See the documentation of [`Pin`] for more +//! information on the practicalities of how to pin a value and how to use that pinned value from a +//! user's perspective without using [`unsafe`]. //! //! The rest of this documentation is intended to be the source of truth for users of [`Pin`] -//! in unsafe code; users of [`Pin`] in safe code do not need to read it in detail. +//! that are implementing the [`unsafe`] pieces of an interface that relies on pinning for validity; +//! users of [`Pin`] in safe code do not need to read it in detail. //! //! There are several sections to this documentation: //! //! * [What is "*moving*"?][what-is-moving] //! * [What is "pinning"?][what-is-pinning] +//! * [Address sensitivity, AKA "when do we need pinning?"][address-sensitive-values] //! * [Examples of types with address-sensitive states][address-sensitive-examples] //! * [Self-referential struct][self-ref] //! * [Intrusive, doubly-linked list][linked-list] @@ -90,8 +115,8 @@ //! to remain *valid* and *located at the same place in memory* from the time it is pinned until its //! [`drop`] is called. //! -//! ## Address-sensitive values, AKA "why we need pinning" -//! [address-sensitive-values]: self#address-sensitive-values +//! ## Address-sensitive values, AKA "when we need pinning" +//! [address-sensitive-values]: self#address-sensitive-values-aka-when-we-need-pinning //! //! Most values in Rust are entirely okay with being *moved* around at-will. //! Types for which it is *always* the case that *any* value of that type can be @@ -105,9 +130,9 @@ //! //! As a motivating example of a type which may become address-sensitive, consider a type which //! contains a pointer to another piece of its own data, *i.e.* a "self-referential" type. In order -//! such a type to be implemented soundly, the pointer which points into `self`'s data must be +//! for such a type to be implemented soundly, the pointer which points into `self`'s data must be //! proven valid whenever it is accessed. But if that value is *moved*, the pointer will still -//! point to the old location that the value was located and not into the new location of `self`, +//! point to the old address where the value was located and not into the new location of `self`, //! thus becoming invalid. A key example of such self-referrential types are the state machines //! generated by the compiler to implement [`Future`] for `async fn`s. //! @@ -122,7 +147,7 @@ //! assume that the address of the value is stable //! * e.g. subsequent calls to [`poll`] //! 4. Before the value is invalidated (e.g. deallocated), it is *dropped*, giving it a chance to -//! "unregister"/clear outstanding pointers to itself +//! notify anything with pointers to itself that those pointers will be invalidated //! * e.g. [`drop`]ping the [`Future`] //! //! There are two possible ways to ensure the invariants required for 2. and 3. above (which @@ -229,8 +254,9 @@ //! #[derive(Default)] //! struct AddrTracker { //! prev_addr: Option, -//! // remove auto-implemented Unpin bound to mark this type as having some -//! // address-sensitive state. This is discussed more below. +//! // remove auto-implemented `Unpin` bound to mark this type as having some +//! // address-sensitive state. This is essential for our expected pinning +//! // guarantees to work, and is discussed more below. //! _pin: PhantomPinned, //! } //! @@ -273,25 +299,52 @@ //! //! The vast majority of Rust types have no address-sensitive states; these types //! implement the [`Unpin`] auto-trait, which cancels the restrictive effects of -//! [`Pin

`]. When [`T: Unpin`][Unpin], [Pin]<[Box]\> and -//! [`Box`] function identically, as do [Pin]<[&mut] T> and -//! [`&mut T`]. +//! [`Pin`] when the *pointee* type `T` is [`Unpin`]. When [`T: Unpin`][Unpin], +//! [Pin]<[Box]\> functions identically to a non-pinning [`Box`]; similarly, +//! [Pin]<[&mut] T> would impose no additional restrictions above a regular [`&mut T`]. //! -//! This includes all of the primitive types, like [`bool`], [`i32`], and [&]T, -//! as well as any other type consisting only of those types. +//! Note that the interaction between a [`Pin`] and [`Unpin`] is through the type of the +//! **pointee** value, [`::Target`][Target]. Whether the `Ptr` type itself +//! implements [`Unpin`] does not affect the behavior of a [`Pin`]. For example, whether or not +//! [`Box`] is [`Unpin`] has no effect on the behavior of [Pin]<[Box]\>, because +//! `T` is the type of the pointee value, not [`Box`]. So, whether `T` implements [`Unpin`] is +//! the thing that will affect the behavior of the [Pin]<[Box]\>. +//! +//! Builtin types that are [`Unpin`] include all of the primitive types, like [`bool`], [`i32`], +//! and [`f32`], references ([&]T and [&mut] T), etc., as well as many +//! core and standard library types like [`Box`], [`String`], and more. +//! These types are marked [`Unpin`] because they do not have an ddress-sensitive state like the +//! ones we discussed above. If they did have such a state, those parts of their interface would be +//! unsound without being expressed through pinning, and they would then need to not +//! implement [`Unpin`]. +//! +//! The compiler (and users!) is free to take the conservative stance of marking types as [`Unpin`] +//! by default. This is because if a type implements [`Unpin`], then it is unsound for [`unsafe`] +//! code to assume that type is truly pinned, *even* when viewed through a "pinning" pointer! It is +//! the responsibility of *the implementor of [`unsafe`] code that relies upon pinning for +//! soundness* (you, in this case!) to ensure that all the types which that code expects to be truly +//! pinned do not implement [`Unpin`]. //! //! Like other auto-traits, the compiler will automatically determine that a type implements -//! [`Unpin`] if all its fields also implement [`Unpin`]. If you are building a type which is built +//! [`Unpin`] if all its fields also implement [`Unpin`]. If you are building a type which consists //! of only [`Unpin`] types but has an address-sensistive state and thus should not itself -//! implement [`Unpin`], you can opt out of [`Unpin`] via adding a field with the -//! [`PhantomPinned`] marker type, as we did with our latest `AddrTracker` example above. +//! implement [`Unpin`], you must opt out of [`Unpin`] via adding a field with the +//! [`PhantomPinned`] marker type, as we did with our latest `AddrTracker` example above. Without +//! doing this, you must not rely on the pinning guarantees to apply to your type! //! -//! Note that the interaction between a [`Pin`] and [`Unpin`] is through the **pointee type** -//! of the value behind the `Ptr`, [`::Target`][Target]. Whether the `Ptr` type itself -//! implements [`Unpin`] does not affect the behavior of a [`Pin`]. For example, whether or not -//! [`Box`] is [`Unpin`] has no effect on the behavior of [Pin]<[Box]\> because -//! `T` is the type of the pointee value, not [`Box`]. So, whether `T` implements [`Unpin`] is -//! the thing that will affect the behavior of the [Pin]<[Box]\>. +//! If you have reason to pin a value of a type that implements [`Unpin`] such that pinning-related +//! guarantees actually are respected, you'll need to create your own wrapper type which itself +//! opts out of implementing [`Unpin`] and contains a sub-field with the [`Unpin`] type that you +//! want to pin. +//! +//! Exposing access to the inner field which you want to remain pinned must then be carefully +//! considered as well! Remember, exposing a method that gives access to a +//! [Pin]<[&mut] InnerT>> where `InnerT: [Unpin]` would allow safe code to trivially +//! move the inner value out of that pinning pointer, which is precisely what you're seeking to +//! prevent! Exposing a field of a pinned value through a pinning pointer is called "projecting" +//! a pin, and the more general case of deciding in which cases a pin should be able to be +//! projected or not is called "structural pinning." We will go into more detail about this +//! [below][structural-pinning]. //! //! # Examples of address-sensitive types //! [address-sensitive-examples]: #examples-of-address-sensitive-types @@ -308,7 +361,7 @@ //! we could imagine being used to track a sliding window of `data` in parser //! code. //! -//! As mentioned before, this pattern is used extensively by compiler-generated +//! As mentioned before, this pattern is also used extensively by compiler-generated //! [`Future`]s. //! //! ```rust @@ -379,27 +432,39 @@ //! ## An intrusive, doubly-linked list //! [linked-list]: #an-intrusive-doubly-linked-list //! -//! In an intrusive doubly-linked list, the collection does not actually allocate the memory for the -//! nodes itself. Allocation is controlled by the clients, and nodes can live on a stack frame -//! that lives shorter than the collection does provided the nodes are removed from the -//! collection before returning. +//! In an intrusive doubly-linked list, the collection itself does not own the memory in which +//! each of its elements is stored. Instead, each client is free to allocate space for elements it +//! adds to the list in whichever manner it likes, including on the stack! Elements can live on a +//! stack frame that lives shorter than the collection does provided the elements that live in a +//! given stack frame are removed from the list before going out of scope. +//! +//! To make such an intrusive data structure work, every element stores pointers to its predecessor +//! and successor within its own data, rather than having the list structure itself managing those +//! pointers. It is in this sense that the structure is "intrusive": the details of how an +//! element is stored within the larger structure "intrudes" on the implementation of the element +//! type itself! //! //! The full implementation details of such a data structure are outside the scope of this //! documentation, but we will discuss how [`Pin`] can help to do so. //! -//! To make such an intrusive data structure work, every element stores pointers to its predecessor -//! and successor within its own data, rather than having the list structure itself manage those -//! pointers. Elements can only be added when they are pinned, because moving the elements -//! around would invalidate the pointers to it which are contained in the element ahead and behind -//! it. Moreover, the [`Drop`][Drop] implementation of the element types themselves will in some -//! way patch the pointers of its predecessor and successor elements to remove itself from the list. +//! Using such an intrusive pattern, elements may only be added when they are pinned. If we think +//! about the consequences of adding non-pinned values to such a list, this becomes clear: //! -//! Crucially, this means we have to be able to rely on [`drop`] always being called before that +//! *Moving* or otherwise invalidating an element's data would invalidate the pointers back to it +//! which are stored in the elements ahead and behind it. Thus, in order to soundly dereference +//! the pointers stored to the next and previous elements, we must satisfy the guarantee that +//! nothing has invalidated those pointers (which point to data which we do not own). +//! +//! Moreover, the [`Drop`][Drop] implementation of each element must in some way notify its +//! predecessor and successor elements that it should be removed from the list before it is fully +//! destroyed, otherwise the pointers back to it would again become invalidated. +//! +//! Crucially, this means we have to be able to rely on [`drop`] always being called before an //! element is invalidated. If an element could be deallocated or otherwise invalidated without -//! calling [`drop`], the pointers into it which are stored in its neighboring elements would +//! calling [`drop`], the pointers to it which are stored in its neighboring elements would //! become invalid, which would break the data structure. //! -//! Therefore, we rely on [the `Drop` guarantee][drop-guarantee] which comes with pinning data, +//! Therefore, pinning data also comes with [the "`Drop` guarantee"][drop-guarantee]. //! //! # Subtle details and the `Drop` guarantee //! [subtle-details]: self#subtle-details-and-the-drop-guarantee @@ -586,6 +651,7 @@ //! the new value. //! //! ## Projections and Structural Pinning +//! [structural-pinning]: self#projections-and-structural-pinning //! //! With ordinary structs, it is natural that we want to add *projection* methods //! that select one of the fields: From 6818d9278b10a2b50921e30f00454b99beb207d9 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 26 Sep 2023 20:13:40 +0200 Subject: [PATCH 165/257] improve intro and discussion of pinning as library contract --- library/core/src/pin.rs | 98 +++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 485ec689e343..40dfb3894877 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -10,22 +10,23 @@ //! //! is called "pinning." We would say that a value which satisfies these guarantees has been //! "pinned," in that it has been permanently (until the end of its lifetime) attached to its -//! location in memory. Pinning a value is incredibly useful in that it provides the necessary -//! guarantees[^guarantees] for [`unsafe`] code to be able to dereference raw pointers to the pinned -//! value for the duration it is pinned (which, [as we'll see later][drop-guarantee], is necessarily -//! from the time the value is first pinned until the end of its lifetime). This concept of -//! "pinning" is necessary to implement safe interfaces on top of things like self-referential types -//! and intrusive data structures which cannot currently be modeled in fully safe Rust using only -//! borrow-checked [references][reference]. +//! location in memory, as though pinned to a pinboard. Pinning a value is incredibly useful in +//! that it provides the necessary guarantees[^guarantees] for [`unsafe`] code to be able to +//! dereference raw pointers to the pinned value for the duration it is pinned (which, +//! [as we'll see later][drop-guarantee], is necessarily from the time the value is first pinned +//! until the end of its lifetime). This concept of "pinning" is necessary to implement safe +//! interfaces on top of things like self-referential types and intrusive data structures which +//! cannot currently be modeled in fully safe Rust using only borrow-checked +//! [references][reference]. //! //! "Pinning" allows us to put a *value* which exists at some location in memory into a state where //! safe code cannot *move* that value to a different location in memory or otherwise invalidate it //! at its current location (unless it implements [`Unpin`], which we will -//! [talk about below][unpin]). Anything that wants to interact with the pinned value in a way that -//! has the potential to violate these guarantees must promise that it will not actually violate -//! them, using the [`unsafe`] keyword to mark that such a promise is upheld by the user and not -//! the compiler. In this way, we can allow other [`unsafe`] code to rely on any pointers that -//! point to the pinned value to be valid to dereference while it is pinned. +//! [talk about below][self#unpin]). Anything that wants to interact with the pinned value in a way +//! that has the potential to violate these guarantees must promise that it will not actually +//! violate them, using the [`unsafe`] keyword to mark that such a promise is upheld by the user +//! and not the compiler. In this way, we can allow other [`unsafe`] code to rely on any pointers +//! that point to the pinned value to be valid to dereference while it is pinned. //! //! [^guarantees]: Pinning on its own does not provide *all* the invariants necessary here. However, //! in order to validly pin a value in the first place, it must already satisfy the other invariants @@ -61,7 +62,7 @@ //! [`Copy`]ing a value from one place in memory to another. In Rust, "move" carries with it the //! semantics of ownership transfer from one variable to another, which is the key difference //! between a [`Copy`] and a move. For the purposes of this module's documentation, however, when -//! we write *move* in italics, we mean *specifically* that the value has moved in the mechanical +//! we write *move* in italics, we mean *specifically* that the value has *moved* in the mechanical //! sense of being located at a new place in memory. //! //! All values in Rust are trivially *moveable*. This means that the address at which a value is @@ -184,12 +185,38 @@ //! will not be *moved* or [otherwise invalidated][subtle-details]. //! //! We call such a [`Pin`]-wrapped pointer a **pinning pointer,** (or pinning reference, or pinning -//! `Box`, etc.) because its existince is the thing that is pinning the underlying pointee in place: -//! it is the metaphorical "pin" securing the data in place on the pinboard (in memory). +//! `Box`, etc.) because its existince is the thing that is *symbolically* pinning the underlying +//! pointee in place: it is the metaphorical "pin" securing the data in place on the pinboard +//! (in memory). //! -//! It is important to stress that the thing in the [`Pin`] is not the value which we want to pin -//! itself, but rather a pointer to that value! A [`Pin`] does not pin the `Ptr` but rather -//! the pointer's ***pointee** value*. +//! Notice that the thing wrapped by [`Pin`] is not the value which we want to pin itself, but +//! rather a pointer to that value! A [`Pin`] does not pin the `Ptr`; instead, it pins the +//! pointer's ***pointee** value*. +//! +//! ### Pinning as a library contract +//! +//! Pinning does not require nor make use of any compiler "magic"[^noalias], only a specific +//! contract between the [`unsafe`] parts of a library API and its users. +//! +//! It is important to stress this point as a user of the [`unsafe`] parts of the [`Pin`] API. +//! Practically, this means that performing the mechanics of "pinning" a value by creating a +//! [`Pin`] to it *does not* actually change the way the compiler behaves towards the +//! inner value! It is possible to use incorrect [`unsafe`] code to create a [`Pin`] to a +//! value which does not actually satisfy the invariants that a pinned value must satisfy, and in +//! this way lead undefined behavior even in (from that point) fully safe code. Similarly, using +//! [`unsafe`], one may get access to a bare [`&mut T`] from a [`Pin`] and +//! juse that to invalidly *move* pinned the value out. It is the job of the user of the +//! [`unsafe`] parts of the [`Pin`] API to ensure these invariants are not violated. +//! +//! This differs from e.g. [`UnsafeCell`] which changes the semantics of a program's compiled +//! output. A [`Pin`] is a handle to a value which we have promised we will not move out of, +//! but Rust still considers all values themselves to be fundamentally moveable through, *e.g.* +//! assignment or [`mem::replace`]. +//! +//! [^noalias]: There is a bit of nuance here that is still being decided about what the aliasing +//! semantics of `Pin<&mut T>` should be, but this is true as of today. +//! +//! ### How [`Pin`] prevents misuse in safe code //! //! In order to accomplish the goal of pinning the pointee value, [`Pin`] restricts access to //! the wrapped `Ptr` type in safe code. Specifically, [`Pin`] disallows the ability to access @@ -199,23 +226,25 @@ //! through that [&mut] T it would be possible to *move* the underlying value out of //! the pointer with [`mem::replace`], etc. //! -//! This promise must be upheld manually by [`unsafe`] code which interacts with the [`Pin`] -//! so that other [`unsafe`] code can rely on the pointee value being *un-moved* and valid. -//! Interfaces that operate on values which are in an address-sensitive state accept an argument -//! like [Pin]<[&mut] T> or [Pin]<[Box]\> to indicate this contract to -//! the caller. +//! As discussed above, this promise must be upheld manually by [`unsafe`] code which interacts +//! with the [`Pin`] so that other [`unsafe`] code can rely on the pointee value being +//! *un-moved* and valid. Interfaces that operate on values which are in an address-sensitive state +//! accept an argument like [Pin]<[&mut] T> or [Pin]<[Box]\> to +//! indicate this contract to the caller. //! -//! [As discussed below][drop-guarantee], this has consequences for running -//! destructors of pinned memory, too. +//! [As discussed below][drop-guarantee], opting in to using pinning guarantees in the interface +//! of an address-sensitive type has consequences for the implementation of some safe traits on +//! that type as well. //! //! ## Interaction between [`Deref`] and [`Pin`] //! //! Since [`Pin`] can wrap any pointer type, it uses [`Deref`] and [`DerefMut`] in //! order to identify the type of the pinned pointee data and provide (restricted) access to it. //! -//! A [`Pin`] where [`Ptr: Deref`][Deref] is a "`Ptr`-style pointer" to a pinned -//! [`P::Target`][Target] – so, a [Pin]<[Box]\> is an owned pointer to a pinned `T`, -//! and a [Pin]<[Rc]\> is a reference-counted pointer to a pinned `T`. +//! A [`Pin`] where [`Ptr: Deref`][Deref] is a "`Ptr`-style pinning pointer" to a pinned +//! [`P::Target`][Target] – so, a [Pin]<[Box]\> is an owned, pinning pointer to a +//! pinned `T`, and a [Pin]<[Rc]\> is a reference-counted, pinning pointer to a +//! pinned `T`. //! //! [`Pin`] also uses the [`::Target`][Target] type information to modify the //! interface it is allowed to provide for interacting with that data (for example, when a @@ -228,17 +257,6 @@ //! types with such "malicious" implementations of [`Deref`]; see [`Pin::new_unchecked`] for //! details. //! -//! ## Pinning as a library contract -//! -//! Pinning does not require any compiler "magic"[^noalias], only a specific contract between the -//! library API and its users. This differs from e.g. [`UnsafeCell`] which changes the semantics of -//! a program's compiled output. A [`Pin`] is a handle to a value which does not allow moving -//! the value out, but Rust still considers all values themselves to be moveable with e.g. -//! [`mem::replace`]. -//! -//! [^noalias]: There is a bit of nuance here that is still being decided about what the aliasing -//! semantics of `Pin<&mut T>` should be, but this is true as of today. -//! //! ## Fixing `AddrTracker` //! //! The guarantee of a stable address is necessary to make our `AddrTracker` example work. When @@ -850,6 +868,7 @@ //! [`std::alloc`]: ../../std/alloc/index.html //! [`Box`]: ../../std/boxed/struct.Box.html //! [Box]: ../../std/boxed/struct.Box.html "Box" +//! [`Box`]: ../../std/boxed/struct.Box.html "Box" //! [`Rc`]: ../../std/rc/struct.Rc.html //! [Rc]: ../../std/rc/struct.Rc.html "rc::Rc" //! [`Vec`]: ../../std/vec/struct.Vec.html @@ -861,6 +880,7 @@ //! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len //! [`VecDeque`]: ../../std/collections/struct.VecDeque.html //! [VecDeque]: ../../std/collections/struct.VecDeque.html "collections::VecDeque" +//! [`String`]: ../../std/string/struct.String.html "String" #![stable(feature = "pin", since = "1.33.0")] From bebbe24a6331ac2ea9f4d77466bedebb99f05709 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 26 Sep 2023 20:14:05 +0200 Subject: [PATCH 166/257] improve `Pin` struct docs and add examples --- library/core/src/pin.rs | 97 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 8 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 40dfb3894877..c1f82bf6f8b8 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -900,14 +900,17 @@ use crate::{ /// A pointer which pins its pointee in place. /// /// [`Pin`] is a wrapper around some kind of pointer `Ptr` which makes that pointer "pin" its -/// pointee value in place, thus preventing the value referenced by that pointer from being moved or -/// otherwise invalidated at that place in memory unless it implements [`Unpin`]. +/// pointee value in place, thus preventing the value referenced by that pointer from being moved +/// or otherwise invalidated at that place in memory unless it implements [`Unpin`]. +/// +/// *See the [`pin` module] documentation for a more thorough exploration of pinning.* /// /// ## Pinning values with [`Pin`] /// /// In order to pin a value, we wrap a *pointer to that value* (of some type `Ptr`) in a /// [`Pin`]. [`Pin`] can wrap any pointer type, forming a promise that the **pointee** -/// will not be *moved* or [otherwise invalidated][subtle-details]. +/// will not be *moved* or [otherwise invalidated][subtle-details]. Note that it is impossible +/// to create or misuse a [`Pin`] which can violate this promise without using [`unsafe`]. /// /// We call such a [`Pin`]-wrapped pointer a **pinning pointer,** (or pinning ref, or pinning /// [`Box`], etc.) because its existince is the thing that is pinning the underlying pointee in @@ -917,13 +920,91 @@ use crate::{ /// itself, but rather a pointer to that value! A [`Pin`] does not pin the `Ptr` but rather /// the pointer's ***pointee** value*. /// -/// `Pin

` is guaranteed to have the same memory layout and ABI as `P`. +/// For the vast majoriy of Rust types, pinning a value of that type will actually have no effect. +/// This is because the vast majority of types implement the [`Unpin`] trait, which entirely opts +/// all values of that type out of pinning-related guarantees. The most common exception +/// to this is the compiler-generated types that implement [`Future`] for the return value +/// of `async fn`s. These compiler-generated [`Future`]s do not implement [`Unpin`] for reasons +/// explained more in the [`pin` module] docs, but suffice it to say they require the guarantees +/// provided by pinning to be implemented soundly. /// -/// *See the [`pin` module] documentation for a more thorough exploration of pinning.* +/// This requirement in the implementation of `async fn`s means that the [`Future`] trait requires +/// any [`Future`] to be pinned in order to call [`poll`] on it. Therefore, when manually polling +/// a future, you will need to pin it first. /// -/// [`pin` module]: self -/// [`Box`]: ../../std/boxed/struct.Box.html -/// [subtle-details]: self#subtle-details-and-the-drop-guarantee +/// ### Pinning a value inside a [`Box`] +/// +/// The simplest and most flexible way to pin a value is to put that value inside a [`Box`] and +/// then turn that [`Box`] into a "pinning [`Box`]" by wrapping it in a [`Pin`]. +/// You can do both of these in a single step using [`Box::pin`]. Let's see an example of using +/// this flow to pin a [`Future`] returned from calling an `async fn`, a common use case +/// as described above. +/// +/// ``` +/// use std::pin::Pin; +/// +/// async fn add_one(x: u32) -> u32 { +/// x + 1 +/// } +/// +/// // Call the async function to get a future back +/// let fut = add_one(42); +/// +/// // Pin the future inside a pinning box +/// let pinned_fut: Pin> = Box::pin(fut); +/// ``` +/// +/// If you have a value which is already boxed, for example a [`Box`][Box], you can pin +/// that value in-place at its current memory address using [`Box::into_pin`]. +/// +/// ``` +/// use std::pin::Pin; +/// use std::future::Future; +/// +/// async fn add_one(x: u32) -> u32 { +/// x + 1 +/// } +/// +/// fn boxed_add_one(x: u32) -> Box> { +/// Box::new(add_one(x)) +/// } +/// +/// let boxed_fut = boxed_add_one(42); +/// +/// // Pin the future inside the existing box +/// let pinned_fut: Pin> = Box::into_pin(boxed_fut); +/// ``` +/// +/// There are similar pinning methods offered on the other standard library smart pointer types +/// as well, like [`Rc`] and [`Arc`]. +/// +/// ### Pinning a value on the stack using [`pin!`] +/// +/// There are some situations where it is desirable or even required (for example, in a `#[no_std]` +/// context where you don't have access to the standard library or allocation in general) to +/// pin a value to its location on the stack. Doing so is possible using the [`pin!`] macro. See +/// its documentation for more. +/// +/// ## Layout and ABI +/// +/// [`Pin`] is guaranteed to have the same memory layout and ABI[^noalias] as `Ptr`. +/// +/// [^noalias]: There is a bit of nuance here that is still being decided about whether the +/// aliasing semantics of `Pin<&mut T>` should be different than `&mut T`, but this is true as of +/// today. +/// +/// [`pin!`]: crate::pin::pin "pin!" +/// [`Future`]: crate::future::Future "Future" +/// [`poll`]: crate::future::Future::poll "Future::poll" +/// [`pin` module]: self "pin module" +/// [`Rc`]: ../../std/rc/struct.Rc.html "Rc" +/// [`Arc`]: ../../std/sync/struct.Arc.html "Arc" +/// [Box]: ../../std/boxed/struct.Box.html "Box" +/// [`Box`]: ../../std/boxed/struct.Box.html "Box" +/// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin "Box::pin" +/// [`Box::into_pin`]: ../../std/boxed/struct.Box.html#method.into_pin "Box::into_pin" +/// [subtle-details]: self#subtle-details-and-the-drop-guarantee "pin subtle details" +/// [`unsafe`]: ../../std/keyword.unsafe.html "keyword unsafe" // // Note: the `Clone` derive below causes unsoundness as it's possible to implement // `Clone` for mutable references. From 82a68171d397804604378a1d0341a965172afe04 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Wed, 27 Sep 2023 11:38:46 +0200 Subject: [PATCH 167/257] fix link in footnote --- library/core/src/pin.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index c1f82bf6f8b8..785044e689d8 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -31,9 +31,9 @@ //! [^guarantees]: Pinning on its own does not provide *all* the invariants necessary here. However, //! in order to validly pin a value in the first place, it must already satisfy the other invariants //! for it to be valid to dereference a pointer to that value while it is pinned, and using the -//! [`Drop` guarantee][drop-guarantee], we can ensure that any interested parties are notified -//! before the value becomes no longer pinned, i.e. when the value goes out of scope and is -//! invalidated. +//! [`Drop` guarantee][self#subtle-details-and-the-drop-guarantee], we can ensure that any +//! interested parties are notified before the value becomes no longer pinned, i.e. when the value +//! goes out of scope and is invalidated. //! //! Note that as long as you don't use [`unsafe`], it's impossible to create or misuse a pinned //! value in a way that will produce unsoundness. See the documentation of [`Pin`] for more From e2e8746bb634a4b58fd6e424985b59b4f6aba089 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Wed, 27 Sep 2023 11:39:08 +0200 Subject: [PATCH 168/257] reword unpin auto impl section --- library/core/src/marker.rs | 14 +++++++------- library/core/src/pin.rs | 30 ++++++++++++------------------ 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index bd19d3cc0e29..5b527dfc9e7e 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -938,13 +938,13 @@ marker_impls! { /// mem::replace(&mut *pinned_string, "other".to_string()); /// ``` /// -/// This trait is automatically implemented for almost every type. The compiler (and you!) is free -/// to take the conservative stance of marking types as [`Unpin`] by default. This is because if a -/// type implements [`Unpin`], then it is unsound for [`unsafe`] code to assume that type is truly -/// pinned, *even* when viewed through a "pinning" pointer! It is the responsibility of the -/// implementor of [`unsafe`] code that relies upon pinning for soundness to ensure that all the -/// types it expects to be truly pinned do not implement [`Unpin`]. For more details, see the -/// [`pin` module] docs! +/// This trait is automatically implemented for almost every type. The compiler is free +/// to take the conservative stance of marking types as [`Unpin`] so long as all of the types that +/// compose its fields are also [`Unpin`]. This is because if a type implements [`Unpin`], then it +/// is unsound for that type's implementation to rely on pinning-related guarantees for soundness, +/// *even* when viewed through a "pinning" pointer! It is the responsibility of the implementor of +/// a type that relies upon pinning for soundness to ensure that type is *not* marked as [`Unpin`] +/// by adding [`PhantomPinned`] field. For more details, see the [`pin` module] docs. /// /// [`mem::replace`]: crate::mem::replace "mem replace" /// [`Pin`]: crate::pin::Pin "Pin" diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 785044e689d8..eb9c0818f176 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -205,7 +205,7 @@ //! value which does not actually satisfy the invariants that a pinned value must satisfy, and in //! this way lead undefined behavior even in (from that point) fully safe code. Similarly, using //! [`unsafe`], one may get access to a bare [`&mut T`] from a [`Pin`] and -//! juse that to invalidly *move* pinned the value out. It is the job of the user of the +//! use that to invalidly *move* pinned the value out. It is the job of the user of the //! [`unsafe`] parts of the [`Pin`] API to ensure these invariants are not violated. //! //! This differs from e.g. [`UnsafeCell`] which changes the semantics of a program's compiled @@ -336,24 +336,18 @@ //! unsound without being expressed through pinning, and they would then need to not //! implement [`Unpin`]. //! -//! The compiler (and users!) is free to take the conservative stance of marking types as [`Unpin`] -//! by default. This is because if a type implements [`Unpin`], then it is unsound for [`unsafe`] -//! code to assume that type is truly pinned, *even* when viewed through a "pinning" pointer! It is -//! the responsibility of *the implementor of [`unsafe`] code that relies upon pinning for -//! soundness* (you, in this case!) to ensure that all the types which that code expects to be truly -//! pinned do not implement [`Unpin`]. +//! The compiler is free to take the conservative stance of marking types as [`Unpin`] so long as +//! all of the types that compose its fields are also [`Unpin`]. This is because if a type +//! implements [`Unpin`], then it is unsound for that type's implementation to rely on +//! pinning-related guarantees for soundness, *even* when viewed through a "pinning" pointer! It is +//! the responsibility of the implementor of a type that relies upon pinning for soundness to +//! ensure that type is *not* marked as [`Unpin`] by adding [`PhantomPinned`] field. This is +//! exactly what we did with our `AddrTracker` example above. Without doing this, you *must not* +//! rely on pinning-related guarantees to apply to your type! //! -//! Like other auto-traits, the compiler will automatically determine that a type implements -//! [`Unpin`] if all its fields also implement [`Unpin`]. If you are building a type which consists -//! of only [`Unpin`] types but has an address-sensistive state and thus should not itself -//! implement [`Unpin`], you must opt out of [`Unpin`] via adding a field with the -//! [`PhantomPinned`] marker type, as we did with our latest `AddrTracker` example above. Without -//! doing this, you must not rely on the pinning guarantees to apply to your type! -//! -//! If you have reason to pin a value of a type that implements [`Unpin`] such that pinning-related -//! guarantees actually are respected, you'll need to create your own wrapper type which itself -//! opts out of implementing [`Unpin`] and contains a sub-field with the [`Unpin`] type that you -//! want to pin. +//! If need to truly pin a value of a foreign or built-in type that implements [`Unpin`], you'll +//! need to create your own wrapper type around the [`Unpin`] type you want to pin and then +//! opts-out of [`Unpin`] using [`PhantomPinned`]. //! //! Exposing access to the inner field which you want to remain pinned must then be carefully //! considered as well! Remember, exposing a method that gives access to a From 7c9c71260e8fded5cb52ca9418b6d99398be2b8c Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Wed, 27 Sep 2023 11:45:54 +0200 Subject: [PATCH 169/257] improve structural Unpin + formatting --- library/core/src/pin.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index eb9c0818f176..b4c2e65ad705 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -753,9 +753,11 @@ //! 1. *Structural [`Unpin`].* A struct can be [`Unpin`] if, and only if, all of its //! structurally-pinned fields are, too. This is [`Unpin`]'s behavior by default. //! However, as a libray author, it is your responsibility not to write something like -//! unsafe impl\ [Unpin] for Struct\ {}. (Adding *any* projection -//! operation requires unsafe code, so the fact that [`Unpin`] is a safe trait does not break -//! the principle that you only have to worry about any of this if you use [`unsafe`].) +//! impl\ [Unpin] for Struct\ {} and then offer a method that provides +//! structural pinning to an inner field of `T`, which may not be [`Unpin`]! (Adding *any* +//! projection operation requires unsafe code, so the fact that [`Unpin`] is a safe trait does +//! not break the principle that you only have to worry about any of this if you use +//! [`unsafe`].) //! //! 2. *Pinned Destruction.* As discussed [above][drop-impl], [`drop`] takes //! [`&mut self`], but the struct (and hence its fields) might have been pinned @@ -764,14 +766,14 @@ //! //! As a consequence, the struct *must not* be [`#[repr(packed)]`][packed]. //! -//! 3. *Structural Notice of Destruction.* You must uphold the the [`Drop` guarantee][drop-guarantee]: -//! once your struct is pinned, the struct's storage cannot be re-used without calling the -//! structurally-pinned fields' destructors, as well. +//! 3. *Structural Notice of Destruction.* You must uphold the the +//! [`Drop` guarantee][drop-guarantee]: once your struct is pinned, the struct's storage cannot +//! be re-used without calling the structurally-pinned fields' destructors, as well. //! //! This can be tricky, as witnessed by [`VecDeque`]: the destructor of [`VecDeque`] //! can fail to call [`drop`] on all elements if one of the destructors panics. This violates -//! the [`Drop` guarantee][drop-guarantee], because it can lead to elements being deallocated without -//! their destructor being called. +//! the [`Drop` guarantee][drop-guarantee], because it can lead to elements being deallocated +//! without their destructor being called. //! //! [`VecDeque`] has no pinning projections, so its destructor is sound. If it wanted //! to provide such structural pinning, its destructor would need to abort the process if any From 252a83b63f91688ce230db3ed713ed5e5e90e15d Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Thu, 28 Sep 2023 15:27:05 +0200 Subject: [PATCH 170/257] add section on manual owning ptr managed solution via @kpreid --- library/core/src/pin.rs | 45 ++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index b4c2e65ad705..7417fa2dd53a 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -163,20 +163,47 @@ //! first option is ruled out. //! //! In order to implement the second option, we must in some way enforce its key invariant, -//! *i.e.* prevent the value from being *moved* or otherwise invalidated. (You may notice this -//! sounds an awful lot like the definition of *pinning* a value). There are two ways by -//! which we might enforce this validity invariant in Rust: +//! *i.e.* prevent the value from being *moved* or otherwise invalidated (you may notice this +//! sounds an awful lot like the definition of *pinning* a value). There a few ways one might be +//! able to enforce this invariant in Rust: //! //! 1. Offer a wholly `unsafe` API to interact with the object, thus requiring every caller to -//! uphold the invariant themselves; or, -//! 2. Leverage the type system to encode and enforce this invariant by presenting a restricted -//! API surface to interact with the object +//! uphold the invariant themselves +//! 2. Store the value that must not be moved behind a carefully managed pointer internal to +//! the object +//! 3. Leverage the type system to encode and enforce this invariant by presenting a restricted +//! API surface to interact with *any* object that requires these invariants //! -//! The first option is quite obviously undesirable, as the `unsafe`ty of the interface will +//! The first option is quite obviously undesirable, as the [`unsafe`]ty of the interface will //! become viral throughout all code that interacts with the object. //! -//! [`Pin`] is an implementation of the second option, allowing us to pin a value in place -//! until its [`drop`] runs in a way that we can depend on it staying valid in `unsafe` code. +//! The second option is a viable solution to the problem for some use cases, in particular +//! for self-referrential types. Under this model, any type that has an address sensitive state +//! would ultimately store its data in something like a [`Box`] and then carefully manage +//! the access to that data internally to ensure no *moves* or other invalidation occur, then +//! provide a safe interface on top. +//! +//! There are a couple of linked disadvantages to using this model. The core issue is a lack +//! of generality. This is an issue first because it means hat each individual type that +//! implements such an interface does so on its own. Each individual developer must themselves +//! think through all the guarantees needed to ensure the API they present is sound. This puts +//! a greater burden on each developer, rather than allowing building a shared understanding of the +//! problem space, encoded into a shared interface to solve it. In addition, and the key issue that +//! drove Rust towards another solution, is that each individual object must assume it is on its +//! own in ensuring that its data does not become *moved* or otherwise invalidated. Since there is +//! no shared contract between values of different types, an object cannot assume that others +//! interacting with it will be a good citizen with its data. Because of this, *composition* of +//! address-sensitive types requires at least a level of pointer indirection (and, practically, a +//! heap allocation) each time a new object is added to the mix. This is particularly a problem +//! when one considers the implications of composing together the [`Future`]s which will eventaully +//! make up an asynchronous task (including address-sensitive `async fn` state machines). +//! It is plausible that there could be many layers of [`Future`]s composed together, including +//! multiple layers of `async fn`s handling different parts of a task, and it was deemed +//! unacceptable to force indirection and allocation for each layer of composition in this case. +//! +//! [`Pin`] is an implementation of the third option. It allows us to solve the issues +//! discussed with the second option by building a *shared contractual language* around the +//! guarantees of "pinning" data. //! //! ## Using [`Pin`] to pin values //! From f2447a6a70149dbef12c41e22d163b0be92588a3 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Thu, 28 Sep 2023 15:34:53 +0200 Subject: [PATCH 171/257] fix typos --- library/core/src/pin.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 7417fa2dd53a..5ddd0c29adf3 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -230,7 +230,7 @@ //! [`Pin`] to it *does not* actually change the way the compiler behaves towards the //! inner value! It is possible to use incorrect [`unsafe`] code to create a [`Pin`] to a //! value which does not actually satisfy the invariants that a pinned value must satisfy, and in -//! this way lead undefined behavior even in (from that point) fully safe code. Similarly, using +//! this way lead to undefined behavior even in (from that point) fully safe code. Similarly, using //! [`unsafe`], one may get access to a bare [`&mut T`] from a [`Pin`] and //! use that to invalidly *move* pinned the value out. It is the job of the user of the //! [`unsafe`] parts of the [`Pin`] API to ensure these invariants are not violated. @@ -423,9 +423,9 @@ //! /// Create a new `Unmovable`. //! /// //! /// To ensure the data doesn't move we place it on the heap behind a pinning Box. -//! /// Note that the data is pinned, but the which is pinning it can itself still be moved. -//! /// This is important because it means we can return the pointer from the function, which -//! /// is itself a kind of move! +//! /// Note that the data is pinned, but the `Pin>` which is pinning it can +//! /// itself still be moved. This is important because it means we can return the pinning +//! /// pointer from the function, which is itself a kind of move! //! fn new() -> Pin> { //! let res = Unmovable { //! data: [0; 64], From 921d37dbd57541e1b6fe97c439cef307d47da7f4 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Thu, 28 Sep 2023 15:37:29 +0200 Subject: [PATCH 172/257] fix imports --- library/core/src/pin.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 5ddd0c29adf3..19beaf2a6745 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -292,9 +292,9 @@ //! to it *cannot* panic. //! //! ``` -//! use core::marker::PhantomPinned; -//! use core::pin::Pin; -//! use core::pin::pin; +//! use std::marker::PhantomPinned; +//! use std::pin::Pin; +//! use std::pin::pin; //! //! #[derive(Default)] //! struct AddrTracker { @@ -327,9 +327,8 @@ //! let mut ptr_to_pinned_tracker: Pin<&mut AddrTracker> = pin!(tracker); //! ptr_to_pinned_tracker.as_mut().check_for_move(); //! -//! // Trying to access `tracker` or pass `ptr_to_pinned_tracker` to anything -//! // that requires mutable access to a non-pinned version of it will no longer -//! // compile +//! // Trying to access `tracker` or pass `ptr_to_pinned_tracker` to anything that requires +//! // mutable access to a non-pinned version of it will no longer compile //! //! // 3. We can now assume that the tracker value will never be moved, thus //! // this will never panic! From 6d5f43d77d2209741fc9103096a554366f38993d Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Thu, 28 Sep 2023 16:11:43 +0200 Subject: [PATCH 173/257] edit new section for typos and better wording --- library/core/src/pin.rs | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 19beaf2a6745..9eea37c62f9a 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -184,22 +184,27 @@ //! provide a safe interface on top. //! //! There are a couple of linked disadvantages to using this model. The core issue is a lack -//! of generality. This is an issue first because it means hat each individual type that -//! implements such an interface does so on its own. Each individual developer must themselves -//! think through all the guarantees needed to ensure the API they present is sound. This puts -//! a greater burden on each developer, rather than allowing building a shared understanding of the -//! problem space, encoded into a shared interface to solve it. In addition, and the key issue that -//! drove Rust towards another solution, is that each individual object must assume it is on its -//! own in ensuring that its data does not become *moved* or otherwise invalidated. Since there is -//! no shared contract between values of different types, an object cannot assume that others -//! interacting with it will be a good citizen with its data. Because of this, *composition* of -//! address-sensitive types requires at least a level of pointer indirection (and, practically, a -//! heap allocation) each time a new object is added to the mix. This is particularly a problem -//! when one considers the implications of composing together the [`Future`]s which will eventaully -//! make up an asynchronous task (including address-sensitive `async fn` state machines). -//! It is plausible that there could be many layers of [`Future`]s composed together, including -//! multiple layers of `async fn`s handling different parts of a task, and it was deemed -//! unacceptable to force indirection and allocation for each layer of composition in this case. +//! of generality. This is an issue because it means that each individual type that implements +//! such an interface does so on its own. Each developer implementing such a type must themselves +//! think through all the guarantees needed to ensure the API they present is sound. We would +//! rather build a shared understanding of the problem space and encode that understanding into a +//! shared interface to solve it which everyone helps validate. +//! +//! In addition, and the key issue that drove Rust towards developing another solution, is that +//! in this model, each individual object must assume it is *on its own* to ensure that its data +//! does not become *moved* or otherwise invalidated. Since there is no shared contract between +//! values of different types, an object cannot assume that others interacting with it will +//! properly respect the invariants around interacting with its data and must therefore protect +//! it from everyone. Because of this, *composition* of address-sensitive types requires at least +//! a level of pointer indirection each time a new object is added to the mix (and, practically, a +//! heap allocation). +//! +//! This is particularly a problem when one considers, for exapmle, the implications of composing +//! together the [`Future`]s which will eventaully make up an asynchronous task (including +//! address-sensitive `async fn` state machines). It is plausible that there could be many layers +//! of [`Future`]s composed together, including multiple layers of `async fn`s handling different +//! parts of a task. It was deemed unacceptable to force indirection and allocation for each layer +//! of composition in this case. //! //! [`Pin`] is an implementation of the third option. It allows us to solve the issues //! discussed with the second option by building a *shared contractual language* around the From de2e748a40c66998397c85216538f89942575dc3 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Thu, 28 Sep 2023 17:22:04 +0200 Subject: [PATCH 174/257] fix typos and edit prose --- library/core/src/pin.rs | 159 +++++++++++++++++++++------------------- 1 file changed, 84 insertions(+), 75 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 9eea37c62f9a..bb315949d8bc 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -36,7 +36,7 @@ //! goes out of scope and is invalidated. //! //! Note that as long as you don't use [`unsafe`], it's impossible to create or misuse a pinned -//! value in a way that will produce unsoundness. See the documentation of [`Pin`] for more +//! value in a way that is unsound. See the documentation of [`Pin`] for more //! information on the practicalities of how to pin a value and how to use that pinned value from a //! user's perspective without using [`unsafe`]. //! @@ -179,8 +179,8 @@ //! //! The second option is a viable solution to the problem for some use cases, in particular //! for self-referrential types. Under this model, any type that has an address sensitive state -//! would ultimately store its data in something like a [`Box`] and then carefully manage -//! the access to that data internally to ensure no *moves* or other invalidation occur, then +//! would ultimately store its data in something like a [`Box`], carefully manage internal +//! access to that data to ensure no *moves* or other invalidation occurs, and finally //! provide a safe interface on top. //! //! There are a couple of linked disadvantages to using this model. The core issue is a lack @@ -190,21 +190,20 @@ //! rather build a shared understanding of the problem space and encode that understanding into a //! shared interface to solve it which everyone helps validate. //! -//! In addition, and the key issue that drove Rust towards developing another solution, is that -//! in this model, each individual object must assume it is *on its own* to ensure that its data -//! does not become *moved* or otherwise invalidated. Since there is no shared contract between -//! values of different types, an object cannot assume that others interacting with it will -//! properly respect the invariants around interacting with its data and must therefore protect -//! it from everyone. Because of this, *composition* of address-sensitive types requires at least -//! a level of pointer indirection each time a new object is added to the mix (and, practically, a -//! heap allocation). +//! In addition, in this model, each individual object must assume it is *on its own* to ensure +//! that its data does not become *moved* or otherwise invalidated. Since there is no shared +//! contract between values of different types, an object cannot assume that others interacting +//! with it will properly respect the invariants around interacting with its data and must +//! therefore protect it from everyone. Because of this, *composition* of address-sensitive types +//! requires at least a level of pointer indirection each time a new object is added to the mix +//! (and, practically, a heap allocation). //! -//! This is particularly a problem when one considers, for exapmle, the implications of composing -//! together the [`Future`]s which will eventaully make up an asynchronous task (including -//! address-sensitive `async fn` state machines). It is plausible that there could be many layers -//! of [`Future`]s composed together, including multiple layers of `async fn`s handling different -//! parts of a task. It was deemed unacceptable to force indirection and allocation for each layer -//! of composition in this case. +//! This is the key thing that drove Rust towards a different model. It is particularly a problem +//! when one considers, for exapmle, the implications of composing together the [`Future`]s which +//! will eventaully make up an asynchronous task (including address-sensitive `async fn` state +//! machines). It is plausible that there could be many layers of [`Future`]s composed together, +//! including multiple layers of `async fn`s handling different parts of a task. It was deemed +//! unacceptable to force indirection and allocation for each layer of composition in this case. //! //! [`Pin`] is an implementation of the third option. It allows us to solve the issues //! discussed with the second option by building a *shared contractual language* around the @@ -237,7 +236,7 @@ //! value which does not actually satisfy the invariants that a pinned value must satisfy, and in //! this way lead to undefined behavior even in (from that point) fully safe code. Similarly, using //! [`unsafe`], one may get access to a bare [`&mut T`] from a [`Pin`] and -//! use that to invalidly *move* pinned the value out. It is the job of the user of the +//! use that to invalidly *move* the pinned value out. It is the job of the user of the //! [`unsafe`] parts of the [`Pin`] API to ensure these invariants are not violated. //! //! This differs from e.g. [`UnsafeCell`] which changes the semantics of a program's compiled @@ -496,7 +495,7 @@ //! *Moving* or otherwise invalidating an element's data would invalidate the pointers back to it //! which are stored in the elements ahead and behind it. Thus, in order to soundly dereference //! the pointers stored to the next and previous elements, we must satisfy the guarantee that -//! nothing has invalidated those pointers (which point to data which we do not own). +//! nothing has invalidated those pointers (which point to data that we do not own). //! //! Moreover, the [`Drop`][Drop] implementation of each element must in some way notify its //! predecessor and successor elements that it should be removed from the list before it is fully @@ -504,7 +503,7 @@ //! //! Crucially, this means we have to be able to rely on [`drop`] always being called before an //! element is invalidated. If an element could be deallocated or otherwise invalidated without -//! calling [`drop`], the pointers to it which are stored in its neighboring elements would +//! calling [`drop`], the pointers to it stored in its neighboring elements would //! become invalid, which would break the data structure. //! //! Therefore, pinning data also comes with [the "`Drop` guarantee"][drop-guarantee]. @@ -513,20 +512,20 @@ //! [subtle-details]: self#subtle-details-and-the-drop-guarantee //! [drop-guarantee]: self#subtle-details-and-the-drop-guarantee //! -//! The purpose of pinning is not *just* to prevent a value from being *moved*, but rather more +//! The purpose of pinning is not *just* to prevent a value from being *moved*, but more //! generally to be able to rely on the pinned value *remaining valid **at a specific place*** in //! memory. //! //! To do so, pinning a value adds an *additional* invariant that must be upheld in order for use -//! of the pinned data to be valid, on top of the ones that must be upheld for a non-pinned value of -//! the same type: +//! of the pinned data to be valid, on top of the ones that must be upheld for a non-pinned value +//! of the same type to be valid: //! //! From the moment a value is pinned by constructing a [`Pin`]ning pointer to it, that value //! must *remain, **valid***, at that same address in memory, *until its [`drop`] handler is //! called.* //! -//! There is some subtlety to this which we have not yet talked about in detail. The invariant above -//! means that, yes, +//! There is some subtlety to this which we have not yet talked about in detail. The invariant +//! described above means that, yes, //! //! 1. The value must not be moved out of its location in memory //! @@ -540,31 +539,31 @@ //! ## `Drop` guarantee //! //! There needs to be a way for a pinned value to notify any code that is relying on its pinned -//! status that it is about to be destroyed, so that such code can remove its address from their -//! data structures or otherwise change their behavior with the knowledge that they can no longer -//! rely on that value existing at the same location. +//! status that it is about to be destroyed. In this way, the dependent code can remove the +//! pinned value's address from its data structures or otherwise change its behavior with the +//! knowledge that it can no longer rely on that value existing at the location it was pinned to. //! //! Thus, in any situation where we may want to overwrite a pinned value, that value's [`drop`] must //! be called beforehand (unless the pinned value implements [`Unpin`], in which case we can ignore //! all of [`Pin`]'s guarantees, as usual). //! -//! The most common storage-reuse situation is when a value on the stack is destroyed as part of a -//! function return, or when heap storage is freed. In both cases, [`drop`] gets run for us -//! by Rust when using standard safe code. However, for heap or otherwise custom-allocated storage, -//! [`unsafe`] code must make sure to call [`ptr::drop_in_place`] before deallocating and re-using -//! said storage. +//! The most common storage-reuse situations occur when a value on the stack is destroyed as part +//! of a function return and when heap storage is freed. In both cases, [`drop`] gets run for us +//! by Rust when using standard safe code. However, for manual heap allocations or otherwise +//! custom-allocated storage, [`unsafe`] code must make sure to call [`ptr::drop_in_place`] before +//! deallocating and re-using said storage. //! -//! However, reuse can happen even if no storage is (de-)allocated. For example, if we had an -//! [`Option`] which contained a `Some(v)` where `v` is a pinned value, then `v` would be -//! invalidated by setting that option to `None`. +//! In addition, storage "re-use"/invalidation can happen even if no storage is (de-)allocated. +//! For example, if we had an [`Option`] which contained a `Some(v)` where `v` is pinned, then `v` +//! would be invalidated by setting that option to `None`. //! -//! Similarly, if a [`Vec`] was used to store pinned values and [`Vec::set_len`] is used to manually -//! "kill" some elements of a vector, all value "killed" would become invalidated. +//! Similarly, if a [`Vec`] was used to store pinned values and [`Vec::set_len`] was used to +//! manually "kill" some elements of a vector, all of the items "killed" would become invalidated, +//! which would be *undefined behavior* if those items were pinned. //! -//! Both of these cases are somewhat contrived, but it is crucial -//! to remember that [`Pin`]ned data *must* be [`drop`]ped before it is invalidated, as a matter of -//! soundness, not just to prevent memory leaks. As a corollary, the following code can *never* be -//! made safe: +//! Both of these cases are somewhat contrived, but it is crucial to remember that [`Pin`]ned data +//! *must* be [`drop`]ped before it is invalidated; not just to prevent memory leaks, but as a +//! matter of soundness. As a corollary, the following code can *never* be made safe: //! //! ```rust //! # use std::mem::ManuallyDrop; @@ -637,20 +636,21 @@ //! //! ### Implementing [`Drop`] for pointer types which will be used as [`Pin`]ning pointers //! -//! It should further be noted that creating a pinning pointer of some type `Ptr` to some underlying -//! `T` which is not `Unpin` *also* carries with it implications on the way that `Ptr` type must -//! implement [`Drop`] (as well as [`Deref`] and [`DerefMut`])! When implementing a pointer type -//! that may be used as a pinning pointer, you must also take the same care described above not to -//! *move* out of or otherwise invalidate the pointee during [`Drop`], [`Deref`], or [`DerefMut`] +//! It should further be noted that creating a pinning pointer of some type `Ptr` *also* carries +//! with it implications on the way that `Ptr` type must implement [`Drop`] +//! (as well as [`Deref`] and [`DerefMut`])! When implementing a pointer type that may be used as +//! a pinning pointer, you must also take the same care described above not to *move* out of or +//! otherwise invalidate the pointee during [`Drop`], [`Deref`], or [`DerefMut`] //! implementations. //! //! ## "Assigning" pinned data //! -//! Although in general it is not valid to swap data through a [`Pin`], or assign from -//! a [`Pin`], for the same reason that a *move* is invalid, there is no particular reason -//! to disallow doing it with specialized functions, as long as they know how to update all -//! uses of the pinned address (and any other `unsafe`-assumed invariants). For [`Unmovable`] ( -//! from the example above) we could write +//! Although in general it is not valid to swap data or assign through a [`Pin`] for the same +//! reason that reusing a pinned object's memory is invalid, it is possible to do validly when +//! implemented with special care for the needs of the exact data structure which is being +//! modified. For example, the assigning function must know how to update all uses of the pinned +//! address (and any other invariants necessary to satisfy validity for that type). For +//! [`Unmovable`] (from the example above), we could write an assignment function like so: //! //! ``` //! # use std::pin::Pin; @@ -689,15 +689,18 @@ //! Even though we can't have the compiler do the assignment for us, it's possible to write //! such specialized functions for types that might need it. //! -//! Note that it _is_ possible to assign through a [`Pin

`] by way of [`Pin::set()`]. This does -//! not violate any guarantees, since it will run [`drop`] on the pointee value before assigning -//! the new value. +//! Note that it _is_ possible to assign generically through a [`Pin

`] by way of [`Pin::set()`]. +//! This does not violate any guarantees, since it will run [`drop`] on the pointee value before +//! assigning the new value. Thus, the [`drop`] implementation still has a chance to perform the +//! necessary notifications to dependent values before the memory location of the original pinned +//! value is overwritten. //! //! ## Projections and Structural Pinning //! [structural-pinning]: self#projections-and-structural-pinning //! -//! With ordinary structs, it is natural that we want to add *projection* methods -//! that select one of the fields: +//! With ordinary structs, it is natural that we want to add *projection* methods that allow +//! borrowing one or more of the inner fields of a struct when the caller has access to a +//! borrow of the whole struct: //! //! ``` //! # struct Field; @@ -712,33 +715,35 @@ //! ``` //! //! When working with address-sensitive types, it's not obvious what the signature of these -//! functions should be. If `field` takes self: [Pin]<[&mut Struct][&mut]>, should it return -//! [`&mut Field`] or [Pin]<[`&mut Field`]>? This question also arises with `enum`s and -//! wrapper types like [`Vec`], [`Box`], and [`RefCell`]. (This question -//! applies just as well to shared references, but we'll examine the more common case -//! of mutable references for illustration). +//! functions should be. If `field` takes self: [Pin]<[&mut Struct][&mut]>, should it +//! return [`&mut Field`] or [Pin]<[`&mut Field`]>? This question also arises with +//! `enum`s and wrapper types like [`Vec`], [`Box`], and [`RefCell`]. (This question +//! applies just as well to shared references, but we'll examine the more common case of mutable +//! references for illustration). //! -//! It turns out that it's up to the author of `Struct` to decide which type the projection -//! should produce. The choice must be *consistent* though: each field should only ever -//! be projected as pinned or unpinned; both together will likely be unsound! +//! It turns out that it's up to the author of `Struct` to decide which type the "projection" +//! should produce. The choice must be *consistent* though: if a pin is projected to a field +//! in one place, then it should very likely not be exposed elsewhere without projecting the +//! pin. //! //! As the author of a data structure, you get to decide for each field whether pinning //! "propagates" to this field or not. Pinning that propagates is also called "structural", //! because it follows the structure of the type. //! -//! The choice of whether to pin depends on how the type is being used. If unsafe code +//! The choice of whether to pin depends on how the type is being used. If [`unsafe`] code //! that consumes [Pin]\<[&mut Struct][&mut]> also needs to take note of //! the address of the field itself, it may be evidence that that field is structurally //! pinned. Unfortunately, there are no hard-and-fast rules. //! //! ### Choosing pinning *not to be* structural for `field`... //! -//! While counter-intuitive, it's actually the easier choice: if a -//! [Pin]<[&mut] Field> is never created, nothing can go wrong (well, so long as no -//! unsound `unsafe` code is written which expects the invariants of such a [`Pin`] to be upheld -//! without actually using pinning to guarantee them)! So, if you decide that some field does not +//! While counter-intuitive, it's actually the easier choice: if you do not expose a +//! [Pin]<[&mut] Field>, then no code must be written assuming that the field is +//! pinned and so nothing can go wrong. So, if you decide that some field does not //! have structural pinning, all you have to ensure is that you never create pinning -//! reference to that field. +//! reference to that field. This does of course also mean that if you decide a field does not +//! have structural pinning, you must not write [`unsafe`] code that assumes (invalidly) that the +//! field *is* structurally pinned! //! //! Fields without structural pinning may have a projection method that turns //! [Pin]<[&mut] Struct> into [`&mut Field`]: @@ -749,15 +754,19 @@ //! # struct Struct { field: Field } //! impl Struct { //! fn field(self: Pin<&mut Self>) -> &mut Field { -//! // This is okay because `field` is never considered pinned. +//! // This is okay because `field` is never considered pinned, therefore we do not +//! // need to uphold any pinning guarantees for this field in particular. Of course, +//! // we must not elsewhere assume this field *is* pinned if we choose to expose +//! // such a method! //! unsafe { &mut self.get_unchecked_mut().field } //! } //! } //! ``` //! -//! You may also impl [Unpin] for Struct {} *even if* the type of `field` -//! is not [`Unpin`]. What that type thinks about pinning is not relevant -//! when no [Pin]<[&mut] Field> is ever created. +//! You may also in this situation impl [Unpin] for Struct {} *even if* the type of +//! `field` is not [`Unpin`]. Since we have explicitly chosen not to care about pinning guarantees +//! for `field`, the way `field`'s type interacts with pinning is no longer relevant in the +//! context of its use in `Struct`. //! //! ### Choosing pinning *to be* structural for `field`... //! From 6e882790b89080e337fa8ff8541023f3b1984c0f Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 3 Oct 2023 09:29:02 +0200 Subject: [PATCH 175/257] `Pin

` -> `Pin` --- library/core/src/pin.rs | 156 +++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 75 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index bb315949d8bc..70c6ddc2a988 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -273,7 +273,7 @@ //! order to identify the type of the pinned pointee data and provide (restricted) access to it. //! //! A [`Pin`] where [`Ptr: Deref`][Deref] is a "`Ptr`-style pinning pointer" to a pinned -//! [`P::Target`][Target] – so, a [Pin]<[Box]\> is an owned, pinning pointer to a +//! [`Ptr::Target`][Target] – so, a [Pin]<[Box]\> is an owned, pinning pointer to a //! pinned `T`, and a [Pin]<[Rc]\> is a reference-counted, pinning pointer to a //! pinned `T`. //! @@ -590,7 +590,7 @@ //! # Implementing an address-sensitive type. //! //! This section goes into detail on important considerations for implementing your own -//! address-sensitive types, which are different from merely using [`Pin

`] in a generic +//! address-sensitive types, which are different from merely using [`Pin`] in a generic //! way. //! //! ## Implementing [`Drop`] for types with address-sensitive states @@ -689,7 +689,7 @@ //! Even though we can't have the compiler do the assignment for us, it's possible to write //! such specialized functions for types that might need it. //! -//! Note that it _is_ possible to assign generically through a [`Pin

`] by way of [`Pin::set()`]. +//! Note that it _is_ possible to assign generically through a [`Pin`] by way of [`Pin::set()`]. //! This does not violate any guarantees, since it will run [`drop`] on the pointee value before //! assigning the new value. Thus, the [`drop`] implementation still has a chance to perform the //! necessary notifications to dependent values before the memory location of the original pinned @@ -1050,7 +1050,7 @@ use crate::{ #[fundamental] #[repr(transparent)] #[derive(Copy, Clone)] -pub struct Pin

{ +pub struct Pin { // FIXME(#93176): this field is made `#[unstable] #[doc(hidden)] pub` to: // - deter downstream users from accessing it (which would be unsound!), // - let the `pin!` macro access it (such a macro requires using struct @@ -1058,7 +1058,7 @@ pub struct Pin

{ // Long-term, `unsafe` fields or macro hygiene are expected to offer more robust alternatives. #[unstable(feature = "unsafe_pin_internals", issue = "none")] #[doc(hidden)] - pub pointer: P, + pub pointer: Ptr, } // The following implementations aren't derived in order to avoid soundness @@ -1068,68 +1068,68 @@ pub struct Pin

{ // See for more details. #[stable(feature = "pin_trait_impls", since = "1.41.0")] -impl PartialEq> for Pin

+impl PartialEq> for Pin where - P::Target: PartialEq, + Ptr::Target: PartialEq, { fn eq(&self, other: &Pin) -> bool { - P::Target::eq(self, other) + Ptr::Target::eq(self, other) } fn ne(&self, other: &Pin) -> bool { - P::Target::ne(self, other) + Ptr::Target::ne(self, other) } } #[stable(feature = "pin_trait_impls", since = "1.41.0")] -impl> Eq for Pin

{} +impl> Eq for Pin {} #[stable(feature = "pin_trait_impls", since = "1.41.0")] -impl PartialOrd> for Pin

+impl PartialOrd> for Pin where - P::Target: PartialOrd, + Ptr::Target: PartialOrd, { fn partial_cmp(&self, other: &Pin) -> Option { - P::Target::partial_cmp(self, other) + Ptr::Target::partial_cmp(self, other) } fn lt(&self, other: &Pin) -> bool { - P::Target::lt(self, other) + Ptr::Target::lt(self, other) } fn le(&self, other: &Pin) -> bool { - P::Target::le(self, other) + Ptr::Target::le(self, other) } fn gt(&self, other: &Pin) -> bool { - P::Target::gt(self, other) + Ptr::Target::gt(self, other) } fn ge(&self, other: &Pin) -> bool { - P::Target::ge(self, other) + Ptr::Target::ge(self, other) } } #[stable(feature = "pin_trait_impls", since = "1.41.0")] -impl> Ord for Pin

{ +impl> Ord for Pin { fn cmp(&self, other: &Self) -> cmp::Ordering { - P::Target::cmp(self, other) + Ptr::Target::cmp(self, other) } } #[stable(feature = "pin_trait_impls", since = "1.41.0")] -impl> Hash for Pin

{ +impl> Hash for Pin { fn hash(&self, state: &mut H) { - P::Target::hash(self, state); + Ptr::Target::hash(self, state); } } -impl> Pin

{ - /// Construct a new `Pin

` around a pointer to some data of a type that +impl> Pin { + /// Construct a new `Pin` around a pointer to some data of a type that /// implements [`Unpin`]. /// /// Unlike `Pin::new_unchecked`, this method is safe because the pointer - /// `P` dereferences to an [`Unpin`] type, which cancels the pinning guarantees. + /// `Ptr` dereferences to an [`Unpin`] type, which cancels the pinning guarantees. /// /// # Examples /// @@ -1143,16 +1143,16 @@ impl> Pin

{ #[inline(always)] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] #[stable(feature = "pin", since = "1.33.0")] - pub const fn new(pointer: P) -> Pin

{ + pub const fn new(pointer: Ptr) -> Pin { // SAFETY: the value pointed to is `Unpin`, and so has no requirements // around pinning. unsafe { Pin::new_unchecked(pointer) } } - /// Unwraps this `Pin

` returning the underlying pointer. + /// Unwraps this `Pin`, returning the underlying pointer. /// - /// This requires that the data inside this `Pin` implements [`Unpin`] so that we - /// can ignore the pinning invariants when unwrapping it. + /// Doing this operation safely requires that the data pointed at by this pinning pointer + /// implemts [`Unpin`] so that we can ignore the pinning invariants when unwrapping it. /// /// # Examples /// @@ -1168,13 +1168,13 @@ impl> Pin

{ #[inline(always)] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] #[stable(feature = "pin_into_inner", since = "1.39.0")] - pub const fn into_inner(pin: Pin

) -> P { + pub const fn into_inner(pin: Pin) -> Ptr { pin.pointer } } -impl Pin

{ - /// Construct a new `Pin

` around a reference to some data of a type that +impl Pin { + /// Construct a new `Pin` around a reference to some data of a type that /// may or may not implement `Unpin`. /// /// If `pointer` dereferences to an `Unpin` type, `Pin::new` should be used @@ -1184,18 +1184,18 @@ impl Pin

{ /// /// This constructor is unsafe because we cannot guarantee that the data /// pointed to by `pointer` is pinned, meaning that the data will not be moved or - /// its storage invalidated until it gets dropped. If the constructed `Pin

` does - /// not guarantee that the data `P` points to is pinned, that is a violation of + /// its storage invalidated until it gets dropped. If the constructed `Pin` does + /// not guarantee that the data `Ptr` points to is pinned, that is a violation of /// the API contract and may lead to undefined behavior in later (safe) operations. /// - /// By using this method, you are making a promise about the `P::Deref` and - /// `P::DerefMut` implementations, if they exist. Most importantly, they + /// By using this method, you are making a promise about the `Ptr::Deref` and + /// `Ptr::DerefMut` implementations, if they exist. Most importantly, they /// must not move out of their `self` arguments: `Pin::as_mut` and `Pin::as_ref` - /// will call `DerefMut::deref_mut` and `Deref::deref` *on the pointer type P* + /// will call `DerefMut::deref_mut` and `Deref::deref` *on the pointer type `Ptr`* /// and expect these methods to uphold the pinning invariants. - /// Moreover, by calling this method you promise that the reference `P` + /// Moreover, by calling this method you promise that the reference `Ptr` /// dereferences to will not be moved out of again; in particular, it - /// must not be possible to obtain a `&mut P::Target` and then + /// must not be possible to obtain a `&mut Ptr::Target` and then /// move out of that reference (using, for example [`mem::swap`]). /// /// For example, calling `Pin::new_unchecked` on an `&'a mut T` is unsafe because @@ -1299,7 +1299,7 @@ impl Pin

{ #[inline(always)] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] #[stable(feature = "pin", since = "1.33.0")] - pub const unsafe fn new_unchecked(pointer: P) -> Pin

{ + pub const unsafe fn new_unchecked(pointer: Ptr) -> Pin { Pin { pointer } } @@ -1312,34 +1312,39 @@ impl Pin

{ /// ruled out by the contract of `Pin::new_unchecked`. #[stable(feature = "pin", since = "1.33.0")] #[inline(always)] - pub fn as_ref(&self) -> Pin<&P::Target> { + pub fn as_ref(&self) -> Pin<&Ptr::Target> { // SAFETY: see documentation on this function unsafe { Pin::new_unchecked(&*self.pointer) } } - /// Unwraps this `Pin

` returning the underlying pointer. + /// Unwraps this `Pin` returning the underlying pointer. /// /// # Safety /// /// This function is unsafe. You must guarantee that you will continue to - /// treat the pointer `P` as pinned after you call this function, so that + /// treat the pointer `Ptr` as pinned after you call this function, so that /// the invariants on the `Pin` type can be upheld. If the code using the - /// resulting `P` does not continue to maintain the pinning invariants that + /// resulting `Ptr` does not continue to maintain the pinning invariants that /// is a violation of the API contract and may lead to undefined behavior in /// later (safe) operations. /// + /// Note that you must be able to guarantee that the data pointed to by `Ptr` + /// will be treated as pinned all the way until its `drop` handler is complete! + /// + /// *For more information, see the [`pin` module docs][self]* + /// /// If the underlying data is [`Unpin`], [`Pin::into_inner`] should be used /// instead. #[inline(always)] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] #[stable(feature = "pin_into_inner", since = "1.39.0")] - pub const unsafe fn into_inner_unchecked(pin: Pin

) -> P { + pub const unsafe fn into_inner_unchecked(pin: Pin) -> Ptr { pin.pointer } } -impl Pin

{ - /// Gets a mutable reference to the pinned value this `Pin

` points to. +impl Pin { + /// Gets a mutable reference to the pinned value this `Pin` points to. /// /// This is a generic method to go from `&mut Pin>` to `Pin<&mut T>`. /// It is safe because, as part of the contract of `Pin::new_unchecked`, @@ -1370,12 +1375,12 @@ impl Pin

{ /// ``` #[stable(feature = "pin", since = "1.33.0")] #[inline(always)] - pub fn as_mut(&mut self) -> Pin<&mut P::Target> { + pub fn as_mut(&mut self) -> Pin<&mut Ptr::Target> { // SAFETY: see documentation on this function unsafe { Pin::new_unchecked(&mut *self.pointer) } } - /// Assigns a new value to the memory location pointed to by the `Pin

`. + /// Assigns a new value to the memory location pointed to by the `Pin`. /// /// This overwrites pinned data, but that is okay: the original pinned value's destructor gets /// run before being overwritten and the new value is also a valid value of the same type, so @@ -1397,9 +1402,9 @@ impl Pin

{ /// [subtle-details]: self#subtle-details-and-the-drop-guarantee #[stable(feature = "pin", since = "1.33.0")] #[inline(always)] - pub fn set(&mut self, value: P::Target) + pub fn set(&mut self, value: Ptr::Target) where - P::Target: Sized, + Ptr::Target: Sized, { *(self.pointer) = value; } @@ -1555,41 +1560,42 @@ impl Pin<&'static T> { } } -impl<'a, P: DerefMut> Pin<&'a mut Pin

> { +impl<'a, Ptr: DerefMut> Pin<&'a mut Pin> { /// Gets `Pin<&mut T>` to the underlying pinned value from this nested `Pin`-pointer. /// /// This is a generic method to go from `Pin<&mut Pin>>` to `Pin<&mut T>`. It is /// safe because the existence of a `Pin>` ensures that the pointee, `T`, cannot /// move in the future, and this method does not enable the pointee to move. "Malicious" - /// implementations of `P::DerefMut` are likewise ruled out by the contract of + /// implementations of `Ptr::DerefMut` are likewise ruled out by the contract of /// `Pin::new_unchecked`. #[unstable(feature = "pin_deref_mut", issue = "86918")] #[must_use = "`self` will be dropped if the result is not used"] #[inline(always)] - pub fn as_deref_mut(self) -> Pin<&'a mut P::Target> { + pub fn as_deref_mut(self) -> Pin<&'a mut Ptr::Target> { // SAFETY: What we're asserting here is that going from // - // Pin<&mut Pin

> + // Pin<&mut Pin> // // to // - // Pin<&mut P::Target> + // Pin<&mut Ptr::Target> // // is safe. // // We need to ensure that two things hold for that to be the case: // - // 1) Once we give out a `Pin<&mut P::Target>`, an `&mut P::Target` will not be given out. - // 2) By giving out a `Pin<&mut P::Target>`, we do not risk of violating `Pin<&mut Pin

>` + // 1) Once we give out a `Pin<&mut Ptr::Target>`, an `&mut Ptr::Target` will not be given out. + // 2) By giving out a `Pin<&mut Ptr::Target>`, we do not risk of violating + // `Pin<&mut Pin>` // - // The existence of `Pin

` is sufficient to guarantee #1: since we already have a - // `Pin

`, it must already uphold the pinning guarantees, which must mean that - // `Pin<&mut P::Target>` does as well, since `Pin::as_mut` is safe. We do not have to rely - // on the fact that P is _also_ pinned. + // The existence of `Pin` is sufficient to guarantee #1: since we already have a + // `Pin`, it must already uphold the pinning guarantees, which must mean that + // `Pin<&mut Ptr::Target>` does as well, since `Pin::as_mut` is safe. We do not have to rely + // on the fact that `Ptr` is _also_ pinned. // - // For #2, we need to ensure that code given a `Pin<&mut P::Target>` cannot cause the - // `Pin

` to move? That is not possible, since `Pin<&mut P::Target>` no longer retains - // any access to the `P` itself, much less the `Pin

`. + // For #2, we need to ensure that code given a `Pin<&mut Ptr::Target>` cannot cause the + // `Pin` to move? That is not possible, since `Pin<&mut Ptr::Target>` no longer retains + // any access to the `Ptr` itself, much less the `Pin`. unsafe { self.get_unchecked_mut() }.as_mut() } } @@ -1609,39 +1615,39 @@ impl Pin<&'static mut T> { } #[stable(feature = "pin", since = "1.33.0")] -impl Deref for Pin

{ - type Target = P::Target; - fn deref(&self) -> &P::Target { +impl Deref for Pin { + type Target = Ptr::Target; + fn deref(&self) -> &Ptr::Target { Pin::get_ref(Pin::as_ref(self)) } } #[stable(feature = "pin", since = "1.33.0")] -impl> DerefMut for Pin

{ - fn deref_mut(&mut self) -> &mut P::Target { +impl> DerefMut for Pin { + fn deref_mut(&mut self) -> &mut Ptr::Target { Pin::get_mut(Pin::as_mut(self)) } } #[unstable(feature = "receiver_trait", issue = "none")] -impl Receiver for Pin

{} +impl Receiver for Pin {} #[stable(feature = "pin", since = "1.33.0")] -impl fmt::Debug for Pin

{ +impl fmt::Debug for Pin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.pointer, f) } } #[stable(feature = "pin", since = "1.33.0")] -impl fmt::Display for Pin

{ +impl fmt::Display for Pin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.pointer, f) } } #[stable(feature = "pin", since = "1.33.0")] -impl fmt::Pointer for Pin

{ +impl fmt::Pointer for Pin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Pointer::fmt(&self.pointer, f) } @@ -1653,10 +1659,10 @@ impl fmt::Pointer for Pin

{ // for other reasons, though, so we just need to take care not to allow such // impls to land in std. #[stable(feature = "pin", since = "1.33.0")] -impl CoerceUnsized> for Pin

where P: CoerceUnsized {} +impl CoerceUnsized> for Pin where Ptr: CoerceUnsized {} #[stable(feature = "pin", since = "1.33.0")] -impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} +impl DispatchFromDyn> for Pin where Ptr: DispatchFromDyn {} /// Constructs a [Pin]<[&mut] T>, by pinning a `value: T` locally. /// From 469c78bcfda4e00cfcdad2bae4793395af444ec2 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 3 Oct 2023 10:36:11 +0200 Subject: [PATCH 176/257] improve `Pin` and `Pin::new` docs --- library/core/src/pin.rs | 90 ++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 70c6ddc2a988..17ab48dfd9af 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -945,8 +945,13 @@ use crate::{ /// /// In order to pin a value, we wrap a *pointer to that value* (of some type `Ptr`) in a /// [`Pin`]. [`Pin`] can wrap any pointer type, forming a promise that the **pointee** -/// will not be *moved* or [otherwise invalidated][subtle-details]. Note that it is impossible -/// to create or misuse a [`Pin`] which can violate this promise without using [`unsafe`]. +/// will not be *moved* or [otherwise invalidated][subtle-details]. Note that it is +/// impossible to create or misuse a [`Pin`] to violate this promise without using [`unsafe`]. +/// If the pointee value's type implements [`Unpin`], we are free to disregard these requirements +/// entirely and can wrap any pointer to that value in [`Pin`] directly via [`Pin::new`]. +/// If the pointee value's type does not implement [`Unpin`], then Rust will not let us use the +/// [`Pin::new`] function directly and we'll need to construct a [`Pin`]-wrapped pointer in one of +/// the more specialized manners discussed below. /// /// We call such a [`Pin`]-wrapped pointer a **pinning pointer,** (or pinning ref, or pinning /// [`Box`], etc.) because its existince is the thing that is pinning the underlying pointee in @@ -956,25 +961,57 @@ use crate::{ /// itself, but rather a pointer to that value! A [`Pin`] does not pin the `Ptr` but rather /// the pointer's ***pointee** value*. /// -/// For the vast majoriy of Rust types, pinning a value of that type will actually have no effect. -/// This is because the vast majority of types implement the [`Unpin`] trait, which entirely opts -/// all values of that type out of pinning-related guarantees. The most common exception -/// to this is the compiler-generated types that implement [`Future`] for the return value -/// of `async fn`s. These compiler-generated [`Future`]s do not implement [`Unpin`] for reasons -/// explained more in the [`pin` module] docs, but suffice it to say they require the guarantees -/// provided by pinning to be implemented soundly. +/// The most common set of types which require pinning related guarantees for soundness are the +/// state machines that implement [`Future`] for the return value of `async fn`s under the +/// hood. These compiler-generated [`Future`]s may contain self-referrential pointers, one of the +/// most common use cases for [`Pin`]. More details on this point are provided in the +/// [`pin` module] docs, but suffice it to say they require the guarantees provided by pinning to +/// be implemented soundly. /// -/// This requirement in the implementation of `async fn`s means that the [`Future`] trait requires -/// any [`Future`] to be pinned in order to call [`poll`] on it. Therefore, when manually polling -/// a future, you will need to pin it first. +/// This requirement from the implementation of `async fn`s means that the [`Future`] trait +/// requires all calls to [`poll`] to use a self: [Pin]\<&mut Self> parameter instead +/// of the usual `&mut self`. Therefore, when manually polling a future, you will need to pin it +/// first. +/// +/// You may notice that `async fn`-generated [`Future`]s are only a small percentage of all +/// [`Future`]s that exist, yet we had to modify the signature of [`poll`] for all [`Future`]s +/// to accommodate them. This is unfortunate, but there is a way that the language attempts to +/// alleviate the extra friction that this API choice incurs: the [`Unpin`] trait. +/// +/// The vast majority of Rust types have no reason to ever care about being pinned. These +/// types implement the [`Unpin`] trait, which entirely opts all values of that type out of +/// pinning-related guarantees. For values of these types, pinning a value by pointing to it with a +/// [`Pin`] will have no actual effect. +/// +/// The reason this distinction exists is exactly to allow APIs like [`Future::poll`] to take a +/// [`Pin`] as an argument for all types while only forcing [`Future`] types that actually +/// care about pinning guarantees pay the ergonomics cost. For the majority of [`Future`] types +/// that don't have a reason to care about being pinned and therefore implement [`Unpin`], the +/// [Pin]\<&mut Self> will act exactly like a regular `&mut Self`, allowing direct +/// access to the underlying value. Only types that *don't* implement [`Unpin`] will be restricted. +/// +/// ### Pinning a value of a type that implements [`Unpin`] +/// +/// If the type of the value you need to "pin" implements [`Unpin`], you can trivially wrap any +/// pointer to that value in a [`Pin`] by calling [`Pin::new`]. +/// +/// ``` +/// use std::pin::Pin; +/// +/// // Create a value of a type that implements `Unpin` +/// let mut unpin_future = std::future::ready(5); +/// +/// // Pin it by creating a pinning mutable reference to it (ready to be `poll`ed!) +/// let my_pinned_unpin_future: Pin<&mut _> = Pin::new(&mut unpin_future); +/// ``` /// /// ### Pinning a value inside a [`Box`] /// -/// The simplest and most flexible way to pin a value is to put that value inside a [`Box`] and -/// then turn that [`Box`] into a "pinning [`Box`]" by wrapping it in a [`Pin`]. -/// You can do both of these in a single step using [`Box::pin`]. Let's see an example of using -/// this flow to pin a [`Future`] returned from calling an `async fn`, a common use case -/// as described above. +/// The simplest and most flexible way to pin a value that does not implement [`Unpin`] is to put +/// that value inside a [`Box`] and then turn that [`Box`] into a "pinning [`Box`]" by wrapping it +/// in a [`Pin`]. You can do both of these in a single step using [`Box::pin`]. Let's see an +/// example of using this flow to pin a [`Future`] returned from calling an `async fn`, a common +/// use case as described above. /// /// ``` /// use std::pin::Pin; @@ -1018,8 +1055,8 @@ use crate::{ /// /// There are some situations where it is desirable or even required (for example, in a `#[no_std]` /// context where you don't have access to the standard library or allocation in general) to -/// pin a value to its location on the stack. Doing so is possible using the [`pin!`] macro. See -/// its documentation for more. +/// pin a value which does not implement [`Unpin`] to its location on the stack. Doing so is +/// possible using the [`pin!`] macro. See its documentation for more. /// /// ## Layout and ABI /// @@ -1032,6 +1069,7 @@ use crate::{ /// [`pin!`]: crate::pin::pin "pin!" /// [`Future`]: crate::future::Future "Future" /// [`poll`]: crate::future::Future::poll "Future::poll" +/// [`Future::poll`]: crate::future::Future::poll "Future::poll" /// [`pin` module]: self "pin module" /// [`Rc`]: ../../std/rc/struct.Rc.html "Rc" /// [`Arc`]: ../../std/sync/struct.Arc.html "Arc" @@ -1137,7 +1175,10 @@ impl> Pin { /// use std::pin::Pin; /// /// let mut val: u8 = 5; - /// // We can pin the value, since it doesn't care about being moved + /// + /// // Since `val` doesn't care about being moved, we can safely create a "facade" `Pin` + /// // which will allow `val` to participate in `Pin`-bound apis without checking that + /// // pinning guarantees are actually upheld. /// let mut pinned: Pin<&mut u8> = Pin::new(&mut val); /// ``` #[inline(always)] @@ -1161,7 +1202,10 @@ impl> Pin { /// /// let mut val: u8 = 5; /// let pinned: Pin<&mut u8> = Pin::new(&mut val); - /// // Unwrap the pin to get a reference to the value + /// + /// // Unwrap the pin to get the underlying mutable reference to the value. We can do + /// // this because `val` doesn't care about being moved, so the `Pin` was just + /// // a "facade" anyway. /// let r = Pin::into_inner(pinned); /// assert_eq!(*r, 5); /// ``` @@ -1317,7 +1361,7 @@ impl Pin { unsafe { Pin::new_unchecked(&*self.pointer) } } - /// Unwraps this `Pin` returning the underlying pointer. + /// Unwraps this `Pin`, returning the underlying `Ptr`. /// /// # Safety /// @@ -1330,7 +1374,7 @@ impl Pin { /// /// Note that you must be able to guarantee that the data pointed to by `Ptr` /// will be treated as pinned all the way until its `drop` handler is complete! - /// + /// /// *For more information, see the [`pin` module docs][self]* /// /// If the underlying data is [`Unpin`], [`Pin::into_inner`] should be used From e0210e6e1d18f748db7d84255b84539f67fd7dc0 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 3 Oct 2023 11:42:46 +0200 Subject: [PATCH 177/257] justify motivation of `Unpin` better --- library/core/src/marker.rs | 9 +++++++++ library/core/src/pin.rs | 35 +++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 5b527dfc9e7e..cf14b8dee370 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -910,6 +910,13 @@ marker_impls! { /// to the pointee value like it normally would, thus allowing the user to do anything that they /// normally could with a non-[`Pin`]-wrapped `Ptr` to that value. /// +/// The idea of this trait is to alleviate the reduced ergonomics of APIs that require the use +/// of [`Pin`] for soundness for some types, but which also want to be used by other types that +/// don't care about pinning. The prime example of such an API is [`Future::poll`]. There are many +/// [`Future`] types that don't care about pinning. These futures can implement `Unpin` and +/// therefore get around the pinning related restrictions in the API, while still allowing the +/// subset of [`Future`]s which *do* require pinning to be implemented soundly. +/// /// For more discussion on the consequences of [`Unpin`] within the wider scope of the pinning /// system, see [the section about `Unpin`] in the [`pin` module]. /// @@ -947,6 +954,8 @@ marker_impls! { /// by adding [`PhantomPinned`] field. For more details, see the [`pin` module] docs. /// /// [`mem::replace`]: crate::mem::replace "mem replace" +/// [`Future`]: crate::future::Future "Future" +/// [`Future::poll`]: crate::future::Future::poll "Future poll" /// [`Pin`]: crate::pin::Pin "Pin" /// [`Pin`]: crate::pin::Pin "Pin" /// [`pin` module]: crate::pin "pin module" diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 17ab48dfd9af..5d05f4bf901b 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -349,7 +349,15 @@ //! implement the [`Unpin`] auto-trait, which cancels the restrictive effects of //! [`Pin`] when the *pointee* type `T` is [`Unpin`]. When [`T: Unpin`][Unpin], //! [Pin]<[Box]\> functions identically to a non-pinning [`Box`]; similarly, -//! [Pin]<[&mut] T> would impose no additional restrictions above a regular [`&mut T`]. +//! [Pin]<[&mut] T> would impose no additional restrictions above a regular +//! [`&mut T`]. +//! +//! The idea of this trait is to alleviate the reduced ergonomics of APIs that require the use +//! of [`Pin`] for soundness for some types, but which also want to be used by other types that +//! don't care about pinning. The prime example of such an API is [`Future::poll`]. There are many +//! [`Future`] types that don't care about pinning. These futures can implement [`Unpin`] and +//! therefore get around the pinning related restrictions in the API, while still allowing the +//! subset of [`Future`]s which *do* require pinning to be implemented soundly. //! //! Note that the interaction between a [`Pin`] and [`Unpin`] is through the type of the //! **pointee** value, [`::Target`][Target]. Whether the `Ptr` type itself @@ -945,15 +953,14 @@ use crate::{ /// /// In order to pin a value, we wrap a *pointer to that value* (of some type `Ptr`) in a /// [`Pin`]. [`Pin`] can wrap any pointer type, forming a promise that the **pointee** -/// will not be *moved* or [otherwise invalidated][subtle-details]. Note that it is -/// impossible to create or misuse a [`Pin`] to violate this promise without using [`unsafe`]. -/// If the pointee value's type implements [`Unpin`], we are free to disregard these requirements -/// entirely and can wrap any pointer to that value in [`Pin`] directly via [`Pin::new`]. -/// If the pointee value's type does not implement [`Unpin`], then Rust will not let us use the -/// [`Pin::new`] function directly and we'll need to construct a [`Pin`]-wrapped pointer in one of -/// the more specialized manners discussed below. +/// will not be *moved* or [otherwise invalidated][subtle-details]. If the pointee value's type +/// implements [`Unpin`], we are free to disregard these requirements entirely and can wrap any +/// pointer to that value in [`Pin`] directly via [`Pin::new`]. If the pointee value's type does +/// not implement [`Unpin`], then Rust will not let us use the [`Pin::new`] function directly and +/// we'll need to construct a [`Pin`]-wrapped pointer in one of the more specialized manners +/// discussed below. /// -/// We call such a [`Pin`]-wrapped pointer a **pinning pointer,** (or pinning ref, or pinning +/// We call such a [`Pin`]-wrapped pointer a **pinning pointer** (or pinning ref, or pinning /// [`Box`], etc.) because its existince is the thing that is pinning the underlying pointee in /// place: it is the metaphorical "pin" securing the data in place on the pinboard (in memory). /// @@ -962,18 +969,18 @@ use crate::{ /// the pointer's ***pointee** value*. /// /// The most common set of types which require pinning related guarantees for soundness are the -/// state machines that implement [`Future`] for the return value of `async fn`s under the -/// hood. These compiler-generated [`Future`]s may contain self-referrential pointers, one of the -/// most common use cases for [`Pin`]. More details on this point are provided in the +/// compiler-generated state machines that implement [`Future`] for the return value of +/// `async fn`s. These compiler-generated [`Future`]s may contain self-referrential pointers, one +/// of the most common use cases for [`Pin`]. More details on this point are provided in the /// [`pin` module] docs, but suffice it to say they require the guarantees provided by pinning to /// be implemented soundly. /// -/// This requirement from the implementation of `async fn`s means that the [`Future`] trait +/// This requirement for the implementation of `async fn`s means that the [`Future`] trait /// requires all calls to [`poll`] to use a self: [Pin]\<&mut Self> parameter instead /// of the usual `&mut self`. Therefore, when manually polling a future, you will need to pin it /// first. /// -/// You may notice that `async fn`-generated [`Future`]s are only a small percentage of all +/// You may notice that `async fn`-sourced [`Future`]s are only a small percentage of all /// [`Future`]s that exist, yet we had to modify the signature of [`poll`] for all [`Future`]s /// to accommodate them. This is unfortunate, but there is a way that the language attempts to /// alleviate the extra friction that this API choice incurs: the [`Unpin`] trait. From f0827b3055eb05e68c680a341043e94b2f7fa502 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 3 Oct 2023 12:03:13 +0200 Subject: [PATCH 178/257] fix broken link --- library/core/src/marker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index cf14b8dee370..edca9003890d 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -918,7 +918,7 @@ marker_impls! { /// subset of [`Future`]s which *do* require pinning to be implemented soundly. /// /// For more discussion on the consequences of [`Unpin`] within the wider scope of the pinning -/// system, see [the section about `Unpin`] in the [`pin` module]. +/// system, see the [section about `Unpin`] in the [`pin` module]. /// /// `Unpin` has no consequence at all for non-pinned data. In particular, [`mem::replace`] happily /// moves `!Unpin` data, which would be immovable when pinned ([`mem::replace`] works for any From d7a886a807a25d097404a6be355bca176632a146 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 3 Oct 2023 13:01:27 +0200 Subject: [PATCH 179/257] update ui tests --- tests/ui/async-await/pin-needed-to-poll-2.stderr | 2 +- .../ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr | 2 +- tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr | 2 +- tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/ui/async-await/pin-needed-to-poll-2.stderr b/tests/ui/async-await/pin-needed-to-poll-2.stderr index 9c1ad32cc2ce..8eb671531e79 100644 --- a/tests/ui/async-await/pin-needed-to-poll-2.stderr +++ b/tests/ui/async-await/pin-needed-to-poll-2.stderr @@ -13,7 +13,7 @@ note: required because it appears within the type `Sleep` | LL | struct Sleep(std::marker::PhantomPinned); | ^^^^^ -note: required by a bound in `Pin::

::new` +note: required by a bound in `Pin::::new` --> $SRC_DIR/core/src/pin.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr b/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr index f5cb3e2b5f80..48fc84618823 100644 --- a/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr +++ b/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr @@ -1,4 +1,4 @@ -error[E0133]: call to unsafe function `Pin::

::new_unchecked` is unsafe and requires unsafe function or block +error[E0133]: call to unsafe function `Pin::::new_unchecked` is unsafe and requires unsafe function or block --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:2:31 | LL | let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; diff --git a/tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr b/tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr index ec985b254b34..1811cd6753ff 100644 --- a/tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr +++ b/tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr @@ -6,7 +6,7 @@ LL | Pin::new(S).x(); | | | required by a bound introduced by this call | -note: required by a bound in `Pin::

::new` +note: required by a bound in `Pin::::new` --> $SRC_DIR/core/src/pin.rs:LL:COL help: consider borrowing here | diff --git a/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index 7c81825e5764..60ab392f55de 100644 --- a/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -52,7 +52,7 @@ LL | Pin::new(x) | = note: consider using the `pin!` macro consider using `Box::pin` if you need to access the pinned value outside of the current scope -note: required by a bound in `Pin::

::new` +note: required by a bound in `Pin::::new` --> $SRC_DIR/core/src/pin.rs:LL:COL error[E0277]: `dyn Future + Send` cannot be unpinned @@ -65,7 +65,7 @@ LL | Pin::new(Box::new(x)) | = note: consider using the `pin!` macro consider using `Box::pin` if you need to access the pinned value outside of the current scope -note: required by a bound in `Pin::

::new` +note: required by a bound in `Pin::::new` --> $SRC_DIR/core/src/pin.rs:LL:COL error[E0308]: mismatched types From 9997114e14df45df370507887d737a3b38c63117 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 3 Oct 2023 13:12:43 +0200 Subject: [PATCH 180/257] improve `Pin::new_unchecked` docs --- library/core/src/pin.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 5d05f4bf901b..b0d777332f44 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1226,21 +1226,24 @@ impl> Pin { impl Pin { /// Construct a new `Pin` around a reference to some data of a type that - /// may or may not implement `Unpin`. + /// may or may not implement [`Unpin`]. /// - /// If `pointer` dereferences to an `Unpin` type, `Pin::new` should be used + /// If `pointer` dereferences to an [`Unpin`] type, [`Pin::new`] should be used /// instead. /// /// # Safety /// /// This constructor is unsafe because we cannot guarantee that the data - /// pointed to by `pointer` is pinned, meaning that the data will not be moved or - /// its storage invalidated until it gets dropped. If the constructed `Pin` does - /// not guarantee that the data `Ptr` points to is pinned, that is a violation of - /// the API contract and may lead to undefined behavior in later (safe) operations. + /// pointed to by `pointer` is pinned. At its core, pinning a value means making the + /// guarantee that the value's data will not be moved nor have its storage invalidated until + /// it gets dropped. For a more thorough explanation of pinning, see the [`pin` module docs]. /// - /// By using this method, you are making a promise about the `Ptr::Deref` and - /// `Ptr::DerefMut` implementations, if they exist. Most importantly, they + /// If the caller that is constructing this `Pin` does not ensure that the data `Ptr` + /// points to is pinned, that is a violation of the API contract and may lead to undefined + /// behavior in later (even safe) operations. + /// + /// By using this method, you are also making a promise about the [`Deref`] and + /// [`DerefMut`] implementations of `Ptr`, if they exist. Most importantly, they /// must not move out of their `self` arguments: `Pin::as_mut` and `Pin::as_ref` /// will call `DerefMut::deref_mut` and `Deref::deref` *on the pointer type `Ptr`* /// and expect these methods to uphold the pinning invariants. @@ -1251,7 +1254,9 @@ impl Pin { /// /// For example, calling `Pin::new_unchecked` on an `&'a mut T` is unsafe because /// while you are able to pin it for the given lifetime `'a`, you have no control - /// over whether it is kept pinned once `'a` ends: + /// over whether it is kept pinned once `'a` ends, and therefore cannot uphold the + /// guarantee that a value, once pinned, remains pinned until it is dropped: + /// /// ``` /// use std::mem; /// use std::pin::Pin; @@ -1285,7 +1290,7 @@ impl Pin { /// // ... /// } /// drop(pin); - + /// /// let content = Rc::get_mut(&mut x).unwrap(); // Potential UB down the road ⚠️ /// // Now, if `x` was the only reference, we have a mutable reference to /// // data that we pinned above, which we could use to move it as we have @@ -1346,6 +1351,7 @@ impl Pin { /// ``` /// /// [`mem::swap`]: crate::mem::swap + /// [`pin` module docs]: self #[lang = "new_unchecked"] #[inline(always)] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] From 058fb50ecdb19cb36b009285e811b87fd7fe254d Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 3 Oct 2023 13:17:26 +0200 Subject: [PATCH 181/257] trim section on managed-box model --- library/core/src/pin.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index b0d777332f44..e106433b44e7 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -183,14 +183,8 @@ //! access to that data to ensure no *moves* or other invalidation occurs, and finally //! provide a safe interface on top. //! -//! There are a couple of linked disadvantages to using this model. The core issue is a lack -//! of generality. This is an issue because it means that each individual type that implements -//! such an interface does so on its own. Each developer implementing such a type must themselves -//! think through all the guarantees needed to ensure the API they present is sound. We would -//! rather build a shared understanding of the problem space and encode that understanding into a -//! shared interface to solve it which everyone helps validate. -//! -//! In addition, in this model, each individual object must assume it is *on its own* to ensure +//! There are a couple of linked disadvantages to using this model. The most significant is that +//! each individual object must assume it is *on its own* to ensure //! that its data does not become *moved* or otherwise invalidated. Since there is no shared //! contract between values of different types, an object cannot assume that others interacting //! with it will properly respect the invariants around interacting with its data and must @@ -198,7 +192,8 @@ //! requires at least a level of pointer indirection each time a new object is added to the mix //! (and, practically, a heap allocation). //! -//! This is the key thing that drove Rust towards a different model. It is particularly a problem +//! Although there were other reason as well, this issue of expensive composition is the key thing +//! that drove Rust towards adopting a different model. It is particularly a problem //! when one considers, for exapmle, the implications of composing together the [`Future`]s which //! will eventaully make up an asynchronous task (including address-sensitive `async fn` state //! machines). It is plausible that there could be many layers of [`Future`]s composed together, From 00506764408132e80658b1312fdd2b72e919119d Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 6 Jan 2024 16:10:03 -0800 Subject: [PATCH 182/257] Rephrase unpin docs in terms of pinning-agnosticness --- library/core/src/marker.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index edca9003890d..561f8ef36ffd 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -899,13 +899,15 @@ marker_impls! { {T: ?Sized} &mut T, } -/// Types that do not need to follow the rules of pinning. +/// Types that do not require any pinning guarantees. /// /// For information on what "pinning" is, see the [`pin` module] documentation. /// -/// Implementing the `Unpin` trait for `T` lifts the restrictions of pinning off that type. -/// This means that, if `T: Unpin`, it cannot be assumed that a value of type `T` will be bound -/// by the invariants that pinning infers, *even* when "pinned" by a [`Pin`] pointing at it. +/// Implementing the `Unpin` trait for `T` expresses the fact that `T` is pinning-agnostic: +/// it shall not expose nor rely on any pinning guarantees. This, in turn, means that a +/// `Pin`-wrapped pointer to such a type can feature a *fully unrestricted* API. +/// In other words, if `T: Unpin`, a value of type `T` will *not* be bound by the invariants +/// which pinning otherwise offers, even when "pinned" by a [`Pin`] pointing at it. /// When a value of type `T` is pointed at by a [`Pin`], [`Pin`] will not restrict access /// to the pointee value like it normally would, thus allowing the user to do anything that they /// normally could with a non-[`Pin`]-wrapped `Ptr` to that value. From 936ceb20f57ebfa4ea2f583ae1d0be488b404895 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 6 Jan 2024 16:15:22 -0800 Subject: [PATCH 183/257] lifetime -> lifespan where relevant. improve docs on as_ref() --- library/core/src/pin.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index e106433b44e7..c47f097b8a31 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -9,12 +9,12 @@ //! 2. More generally, remain *valid* at that same memory location //! //! is called "pinning." We would say that a value which satisfies these guarantees has been -//! "pinned," in that it has been permanently (until the end of its lifetime) attached to its +//! "pinned," in that it has been permanently (until the end of its lifespan) attached to its //! location in memory, as though pinned to a pinboard. Pinning a value is incredibly useful in //! that it provides the necessary guarantees[^guarantees] for [`unsafe`] code to be able to //! dereference raw pointers to the pinned value for the duration it is pinned (which, //! [as we'll see later][drop-guarantee], is necessarily from the time the value is first pinned -//! until the end of its lifetime). This concept of "pinning" is necessary to implement safe +//! until the end of its lifespan). This concept of "pinning" is necessary to implement safe //! interfaces on top of things like self-referential types and intrusive data structures which //! cannot currently be modeled in fully safe Rust using only borrow-checked //! [references][reference]. @@ -126,7 +126,7 @@ //! [`Pin`] is specifically targeted at allowing the implementation of *safe interfaces* around //! types which have some state during which they become "address-sensitive." A value in such an //! "address-sensitive" state is *not* okay with being *moved* around at-will. Such a value must -//! stay *un-moved* and valid during the address-sensitive portion of its lifetime because some +//! stay *un-moved* and valid during the address-sensitive portion of its lifespan because some //! interface is relying on those invariants to be true in order for its implementation to be sound. //! //! As a motivating example of a type which may become address-sensitive, consider a type which @@ -535,7 +535,7 @@ //! but it also implies that, //! //! 2. The memory location that stores the value must not get invalidated or otherwise repurposed -//! during the lifetime of the pinned value until its [`drop`] returns or panics +//! during the lifespan of the pinned value until its [`drop`] returns or panics //! //! This point is subtle but required for intrusive data structures to be implemented soundly. //! @@ -1505,8 +1505,8 @@ impl<'a, T: ?Sized> Pin<&'a T> { /// Note: `Pin` also implements `Deref` to the target, which can be used /// to access the inner value. However, `Deref` only provides a reference /// that lives for as long as the borrow of the `Pin`, not the lifetime of - /// the `Pin` itself. This method allows turning the `Pin` into a reference - /// with the same lifetime as the original `Pin`. + /// the reference contained in the `Pin`. This method allows turning the `Pin` into a reference + /// with the same lifetime as the reference it wraps. /// /// ["pinning projections"]: self#projections-and-structural-pinning #[inline(always)] From 4c25246f3e94002795e7652feac6ef6fa0415de2 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 6 Jan 2024 16:24:03 -0800 Subject: [PATCH 184/257] Clean up guarantees wording We don't need to go into that much depth at this stage --- library/core/src/pin.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index c47f097b8a31..3926a661dce6 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -10,14 +10,13 @@ //! //! is called "pinning." We would say that a value which satisfies these guarantees has been //! "pinned," in that it has been permanently (until the end of its lifespan) attached to its -//! location in memory, as though pinned to a pinboard. Pinning a value is incredibly useful in -//! that it provides the necessary guarantees[^guarantees] for [`unsafe`] code to be able to -//! dereference raw pointers to the pinned value for the duration it is pinned (which, -//! [as we'll see later][drop-guarantee], is necessarily from the time the value is first pinned -//! until the end of its lifespan). This concept of "pinning" is necessary to implement safe -//! interfaces on top of things like self-referential types and intrusive data structures which -//! cannot currently be modeled in fully safe Rust using only borrow-checked -//! [references][reference]. +//! location in memory, as though pinned to a pinboard. Pinning a value is an incredibly useful +//! building block for [unsafe] code to be able to reason about whether a raw pointer to the +//! pinned value is still valid. [As we'll see later][drop-guarantee], this is necessarily from the +//! time the value is first pinned until the end of its lifespan. This concept of "pinning" is +//! necessary to implement safe interfaces on top of things like self-referential types and +//! intrusive data structures which cannot currently be modeled in fully safe Rust using only +//! borrow-checked [references][reference]. //! //! "Pinning" allows us to put a *value* which exists at some location in memory into a state where //! safe code cannot *move* that value to a different location in memory or otherwise invalidate it @@ -28,13 +27,6 @@ //! and not the compiler. In this way, we can allow other [`unsafe`] code to rely on any pointers //! that point to the pinned value to be valid to dereference while it is pinned. //! -//! [^guarantees]: Pinning on its own does not provide *all* the invariants necessary here. However, -//! in order to validly pin a value in the first place, it must already satisfy the other invariants -//! for it to be valid to dereference a pointer to that value while it is pinned, and using the -//! [`Drop` guarantee][self#subtle-details-and-the-drop-guarantee], we can ensure that any -//! interested parties are notified before the value becomes no longer pinned, i.e. when the value -//! goes out of scope and is invalidated. -//! //! Note that as long as you don't use [`unsafe`], it's impossible to create or misuse a pinned //! value in a way that is unsound. See the documentation of [`Pin`] for more //! information on the practicalities of how to pin a value and how to use that pinned value from a From 68bdeddb5cc6ec3ee787a94d7373928048e5b81b Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 7 Jan 2024 00:27:01 +0000 Subject: [PATCH 185/257] Apply suggestions from code review Co-authored-by: Ralf Jung Co-authored-by: Daniel Henry-Mantilla --- library/core/src/pin.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 3926a661dce6..a8ef5949e7a8 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -146,10 +146,10 @@ //! There are two possible ways to ensure the invariants required for 2. and 3. above (which //! apply to any address-sensitive type, not just self-referrential types) do not get broken. //! -//! 1. Have the value detect when it is moved and update all the pointers that point to itself +//! 1. Have the value detect when it is moved and update all the pointers that point to itself. //! 2. Guarantee that the address of the value does not change (and that memory is not re-used //! for anything else) during the time that the pointers to it are expected to be valid to -//! dereference +//! dereference. //! //! Since, as we discussed, Rust can move values without notifying them that they have moved, the //! first option is ruled out. @@ -203,7 +203,7 @@ //! will not be *moved* or [otherwise invalidated][subtle-details]. //! //! We call such a [`Pin`]-wrapped pointer a **pinning pointer,** (or pinning reference, or pinning -//! `Box`, etc.) because its existince is the thing that is *symbolically* pinning the underlying +//! `Box`, etc.) because its existence is the thing that is conceptually pinning the underlying //! pointee in place: it is the metaphorical "pin" securing the data in place on the pinboard //! (in memory). //! @@ -332,7 +332,7 @@ //! //! ## [`Unpin`] //! -//! The vast majority of Rust types have no address-sensitive states; these types +//! The vast majority of Rust types have no address-sensitive states. These types //! implement the [`Unpin`] auto-trait, which cancels the restrictive effects of //! [`Pin`] when the *pointee* type `T` is [`Unpin`]. When [`T: Unpin`][Unpin], //! [Pin]<[Box]\> functions identically to a non-pinning [`Box`]; similarly, From 6553d0d55119512369f3636c93ae86c2e609bd7b Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 6 Jan 2024 16:28:07 -0800 Subject: [PATCH 186/257] punctuation in parens --- library/core/src/pin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index a8ef5949e7a8..600208132ba8 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -714,7 +714,7 @@ //! return [`&mut Field`] or [Pin]<[`&mut Field`]>? This question also arises with //! `enum`s and wrapper types like [`Vec`], [`Box`], and [`RefCell`]. (This question //! applies just as well to shared references, but we'll examine the more common case of mutable -//! references for illustration). +//! references for illustration) //! //! It turns out that it's up to the author of `Struct` to decide which type the "projection" //! should produce. The choice must be *consistent* though: if a pin is projected to a field @@ -792,7 +792,7 @@ //! structural pinning to an inner field of `T`, which may not be [`Unpin`]! (Adding *any* //! projection operation requires unsafe code, so the fact that [`Unpin`] is a safe trait does //! not break the principle that you only have to worry about any of this if you use -//! [`unsafe`].) +//! [`unsafe`]) //! //! 2. *Pinned Destruction.* As discussed [above][drop-impl], [`drop`] takes //! [`&mut self`], but the struct (and hence its fields) might have been pinned From 6a54ed71c04c1c79fa3060040742f44d5b04ea16 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 6 Jan 2024 16:33:07 -0800 Subject: [PATCH 187/257] valid --- library/core/src/pin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 600208132ba8..18113c6def86 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -105,7 +105,7 @@ //! [what-is-pinning]: self#what-is-pinning //! //! We say that a value has been *pinned* when it has been put into a state where it is guaranteed -//! to remain *valid* and *located at the same place in memory* from the time it is pinned until its +//! to remain *located at the same place in memory* from the time it is pinned until its //! [`drop`] is called. //! //! ## Address-sensitive values, AKA "when we need pinning" From a573c7c409ca335aaef1f54bbff0c5816e38cad6 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 6 Jan 2024 16:37:58 -0800 Subject: [PATCH 188/257] footnote on dropping futures --- library/core/src/pin.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 18113c6def86..3af023433076 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -126,7 +126,7 @@ //! for such a type to be implemented soundly, the pointer which points into `self`'s data must be //! proven valid whenever it is accessed. But if that value is *moved*, the pointer will still //! point to the old address where the value was located and not into the new location of `self`, -//! thus becoming invalid. A key example of such self-referrential types are the state machines +//! thus becoming invalid. A key example of such self-referential types are the state machines //! generated by the compiler to implement [`Future`] for `async fn`s. //! //! Such types that have an *address-sensitive* state usually follow a lifecycle @@ -141,7 +141,7 @@ //! * e.g. subsequent calls to [`poll`] //! 4. Before the value is invalidated (e.g. deallocated), it is *dropped*, giving it a chance to //! notify anything with pointers to itself that those pointers will be invalidated -//! * e.g. [`drop`]ping the [`Future`] +//! * e.g. [`drop`]ping the [`Future`] [^pin-drop-future] //! //! There are two possible ways to ensure the invariants required for 2. and 3. above (which //! apply to any address-sensitive type, not just self-referrential types) do not get broken. @@ -196,6 +196,9 @@ //! discussed with the second option by building a *shared contractual language* around the //! guarantees of "pinning" data. //! +//! [^pin-drop-future]: Futures themselves do not ever need to notify other bits of code that +//! they are being dropped, however data structures like stack-based intrusive linked lists do. +//! //! ## Using [`Pin`] to pin values //! //! In order to pin a value, we wrap a *pointer to that value* (of some type `Ptr`) in a From b1830f130a0ccd06cd43ac5f2c09ed669e5b1d3b Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 6 Jan 2024 16:54:01 -0800 Subject: [PATCH 189/257] clean up structural pinning --- library/core/src/pin.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 3af023433076..15f5d7ce19c7 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -728,17 +728,19 @@ //! "propagates" to this field or not. Pinning that propagates is also called "structural", //! because it follows the structure of the type. //! -//! The choice of whether to pin depends on how the type is being used. If [`unsafe`] code -//! that consumes [Pin]\<[&mut Struct][&mut]> also needs to take note of -//! the address of the field itself, it may be evidence that that field is structurally -//! pinned. Unfortunately, there are no hard-and-fast rules. +//! This choice depends on what guarantees you need from the field for your [`unsafe`] code to work. +//! If the field is itself address-sensitive, or participates in the parent struct's address +//! sensitivity, it will need to be structurally pinned. +//! +//! A useful test is if [`unsafe`] code that consumes [Pin]\<[&mut Struct][&mut]> +//! also needs to take note of the address of the field itself, it may be evidence that that field +//! is structurally pinned. Unfortunately, there are no hard-and-fast rules. //! //! ### Choosing pinning *not to be* structural for `field`... //! -//! While counter-intuitive, it's actually the easier choice: if you do not expose a -//! [Pin]<[&mut] Field>, then no code must be written assuming that the field is -//! pinned and so nothing can go wrong. So, if you decide that some field does not -//! have structural pinning, all you have to ensure is that you never create pinning +//! While counter-intuitive, it's often the easier choice: if you do not expose a +//! [Pin]<[&mut] Field>, you do not need to be careful about other code +//! moving out of that field, you just have to ensure is that you never create pinning //! reference to that field. This does of course also mean that if you decide a field does not //! have structural pinning, you must not write [`unsafe`] code that assumes (invalidly) that the //! field *is* structurally pinned! From df6d44961d6f05b7c51189dcce3b05c4fb9ced63 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 7 Jan 2024 08:33:14 -0800 Subject: [PATCH 190/257] Update library/core/src/pin.rs Co-authored-by: Ralf Jung --- library/core/src/pin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 15f5d7ce19c7..ef29e3f373ab 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -790,7 +790,7 @@ //! //! Structural pinning comes with a few extra requirements: //! -//! 1. *Structural [`Unpin`].* A struct can be [`Unpin`] if, and only if, all of its +//! 1. *Structural [`Unpin`].* A struct can be [`Unpin`] only if all of its //! structurally-pinned fields are, too. This is [`Unpin`]'s behavior by default. //! However, as a libray author, it is your responsibility not to write something like //! impl\ [Unpin] for Struct\ {} and then offer a method that provides From 7fd841c098e58a69808610e4e89366c03c5621fc Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 7 Jan 2024 08:57:23 -0800 Subject: [PATCH 191/257] link --- library/core/src/pin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index ef29e3f373ab..bb6c81a486a5 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -11,7 +11,7 @@ //! is called "pinning." We would say that a value which satisfies these guarantees has been //! "pinned," in that it has been permanently (until the end of its lifespan) attached to its //! location in memory, as though pinned to a pinboard. Pinning a value is an incredibly useful -//! building block for [unsafe] code to be able to reason about whether a raw pointer to the +//! building block for [`unsafe`] code to be able to reason about whether a raw pointer to the //! pinned value is still valid. [As we'll see later][drop-guarantee], this is necessarily from the //! time the value is first pinned until the end of its lifespan. This concept of "pinning" is //! necessary to implement safe interfaces on top of things like self-referential types and From 3acc5a0da327777b2e4a39a500a17546545dc569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 7 Jan 2024 18:22:47 +0100 Subject: [PATCH 192/257] effects: support ~const in assoc fns in trait impls --- compiler/rustc_ast_lowering/src/item.rs | 29 ++++++++------- .../const-impl-trait.stderr | 37 ++++++++++--------- .../tilde-const-assoc-fn-in-trait-impl.rs | 29 +++++++++++++++ ...=> tilde-const-inherent-assoc-const-fn.rs} | 0 4 files changed, 65 insertions(+), 30 deletions(-) create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-assoc-fn-in-trait-impl.rs rename tests/ui/rfcs/rfc-2632-const-trait-impl/{tilde_const_on_impl_bound.rs => tilde-const-inherent-assoc-const-fn.rs} (100%) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index c618953461cf..a2950a5011f5 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -12,6 +12,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::PredicateOrigin; use rustc_index::{Idx, IndexSlice, IndexVec}; +use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{kw, sym, Ident}; @@ -572,23 +573,25 @@ impl<'hir> LoweringContext<'_, 'hir> { // This is used to track which lifetimes have already been defined, // and which need to be replicated when lowering an async fn. - match parent_hir.node().expect_item().kind { + let generics = match parent_hir.node().expect_item().kind { hir::ItemKind::Impl(impl_) => { self.is_in_trait_impl = impl_.of_trait.is_some(); + &impl_.generics } - hir::ItemKind::Trait(_, _, generics, _, _) if self.tcx.features().effects => { - self.host_param_id = generics - .params - .iter() - .find(|param| { - matches!( - param.kind, - hir::GenericParamKind::Const { is_host_effect: true, .. } - ) - }) - .map(|param| param.def_id); + hir::ItemKind::Trait(_, _, generics, _, _) => generics, + kind => { + span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr()) } - _ => {} + }; + + if self.tcx.features().effects { + self.host_param_id = generics + .params + .iter() + .find(|param| { + matches!(param.kind, hir::GenericParamKind::Const { is_host_effect: true, .. }) + }) + .map(|param| param.def_id); } match ctxt { diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr index ddedf8f1d8d2..d0ca1b19ad17 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr @@ -1,27 +1,30 @@ -error[E0277]: can't compare `impl PartialEq + Destruct + Copy` with `impl PartialEq + Destruct + Copy` - --> $DIR/const-impl-trait.rs:28:17 +error[E0277]: can't compare `()` with `()` + --> $DIR/const-impl-trait.rs:35:17 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `impl PartialEq + Destruct + Copy == impl PartialEq + Destruct + Copy` +LL | assert!(cmp(&())); + | --- ^^^ no implementation for `() == ()` + | | + | required by a bound introduced by this call | - = help: the trait `~const PartialEq` is not implemented for `impl PartialEq + Destruct + Copy` -note: required by a bound in `Foo::{opaque#0}` - --> $DIR/const-impl-trait.rs:24:22 + = help: the trait `const PartialEq` is not implemented for `()` + = help: the trait `PartialEq` is implemented for `()` +note: required by a bound in `cmp` + --> $DIR/const-impl-trait.rs:12:23 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; - | ^^^^^^^^^^^^^^^^ required by this bound in `Foo::{opaque#0}` +LL | const fn cmp(a: &impl ~const PartialEq) -> bool { + | ^^^^^^^^^^^^^^^^ required by this bound in `cmp` -error[E0277]: can't drop `impl PartialEq + Destruct + Copy` - --> $DIR/const-impl-trait.rs:28:17 +error[E0277]: can't compare `&impl ~const PartialEq` with `&impl ~const PartialEq` + --> $DIR/const-impl-trait.rs:13:7 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `~const Destruct` is not implemented for `impl PartialEq + Destruct + Copy` +LL | a == a + | ^^ no implementation for `&impl ~const PartialEq == &impl ~const PartialEq` | -note: required by a bound in `Foo::{opaque#0}` - --> $DIR/const-impl-trait.rs:24:41 + = help: the trait `~const PartialEq<&impl ~const PartialEq>` is not implemented for `&impl ~const PartialEq` +help: consider dereferencing both sides of the expression | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; - | ^^^^^^^^^^^^^^^ required by this bound in `Foo::{opaque#0}` +LL | *a == *a + | + + error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-assoc-fn-in-trait-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-assoc-fn-in-trait-impl.rs new file mode 100644 index 000000000000..a848b6d2fc9f --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-assoc-fn-in-trait-impl.rs @@ -0,0 +1,29 @@ +// Regression test for issue #119700. +// check-pass + +#![feature(const_trait_impl, effects)] + +#[const_trait] +trait Main { + fn compute() -> u32; +} + +impl const Main for () { + fn compute() -> u32 { + T::generate() + } +} + +#[const_trait] +trait Aux { + fn generate() -> u32; +} + +impl const Aux for () { + fn generate() -> u32 { 1024 } +} + +fn main() { + const _: u32 = <()>::compute::<()>(); + let _ = <()>::compute::<()>(); +} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-inherent-assoc-const-fn.rs similarity index 100% rename from tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-inherent-assoc-const-fn.rs From 7e38b70cc020a0a472c8eafb77cd3fec45f79ae1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 7 Jan 2024 17:11:48 +0000 Subject: [PATCH 193/257] Split note, fix const/static impl trait error --- compiler/rustc_ast_lowering/messages.ftl | 3 +- compiler/rustc_ast_lowering/src/errors.rs | 1 + compiler/rustc_ast_lowering/src/item.rs | 10 +- compiler/rustc_ast_lowering/src/lib.rs | 4 +- .../ui/associated-consts/issue-105330.stderr | 4 +- .../feature-gate-associated_type_bounds.rs | 6 +- ...feature-gate-associated_type_bounds.stderr | 12 +- ...ture-gate-impl_trait_in_fn_trait_return.rs | 4 +- ...-gate-impl_trait_in_fn_trait_return.stderr | 6 +- tests/ui/impl-trait/issues/issue-54600.rs | 2 +- tests/ui/impl-trait/issues/issue-54600.stderr | 4 +- tests/ui/impl-trait/issues/issue-54840.rs | 2 +- tests/ui/impl-trait/issues/issue-54840.stderr | 4 +- tests/ui/impl-trait/issues/issue-58504.rs | 2 +- tests/ui/impl-trait/issues/issue-58504.stderr | 4 +- tests/ui/impl-trait/issues/issue-58956.rs | 4 +- tests/ui/impl-trait/issues/issue-58956.stderr | 8 +- tests/ui/impl-trait/issues/issue-70971.rs | 2 +- tests/ui/impl-trait/issues/issue-70971.stderr | 4 +- tests/ui/impl-trait/issues/issue-79099.rs | 2 +- tests/ui/impl-trait/issues/issue-79099.stderr | 4 +- ...sue-83929-impl-trait-in-generic-default.rs | 4 +- ...83929-impl-trait-in-generic-default.stderr | 8 +- tests/ui/impl-trait/issues/issue-84919.rs | 2 +- tests/ui/impl-trait/issues/issue-84919.stderr | 4 +- tests/ui/impl-trait/issues/issue-86642.rs | 2 +- tests/ui/impl-trait/issues/issue-86642.stderr | 4 +- tests/ui/impl-trait/issues/issue-87295.rs | 2 +- tests/ui/impl-trait/issues/issue-87295.stderr | 4 +- tests/ui/impl-trait/nested_impl_trait.rs | 2 +- tests/ui/impl-trait/nested_impl_trait.stderr | 4 +- tests/ui/impl-trait/where-allowed.rs | 74 ++++----- tests/ui/impl-trait/where-allowed.stderr | 148 +++++++++++++----- tests/ui/issues/issue-47715.rs | 8 +- tests/ui/issues/issue-47715.stderr | 16 +- .../type-alias-impl-trait-fn-type.rs | 2 +- .../type-alias-impl-trait-fn-type.stderr | 4 +- tests/ui/typeck/issue-104513-ice.rs | 2 +- tests/ui/typeck/issue-104513-ice.stderr | 4 +- 39 files changed, 257 insertions(+), 129 deletions(-) diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index fd94e7e9341d..f4e3086f2b58 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -106,7 +106,8 @@ ast_lowering_misplaced_double_dot = .note = only allowed in tuple, tuple struct, and slice patterns ast_lowering_misplaced_impl_trait = - `impl Trait` only allowed in function and inherent method argument and return types, not in {$position} + `impl Trait` is not allowed in {$position} + .note = `impl Trait` is only allowed in arguments and return types of functions and methods ast_lowering_misplaced_relax_trait_bound = `?Trait` bounds are only permitted at the point where a type parameter is declared diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 710690d0d86a..faa22eece380 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -90,6 +90,7 @@ pub enum AssocTyParenthesesSub { #[derive(Diagnostic)] #[diag(ast_lowering_misplaced_impl_trait, code = "E0562")] +#[note] pub struct MisplacedImplTrait<'a> { #[primary_span] pub span: Span, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index c618953461cf..7fd7d9489b06 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -182,7 +182,8 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs) } ItemKind::Static(box ast::StaticItem { ty: t, mutability: m, expr: e }) => { - let (ty, body_id) = self.lower_const_item(t, span, e.as_deref()); + let (ty, body_id) = + self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy); hir::ItemKind::Static(ty, *m, body_id) } ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => { @@ -191,7 +192,9 @@ impl<'hir> LoweringContext<'_, 'hir> { Const::No, id, &ImplTraitContext::Disallowed(ImplTraitPosition::Generic), - |this| this.lower_const_item(ty, span, expr.as_deref()), + |this| { + this.lower_const_item(ty, span, expr.as_deref(), ImplTraitPosition::ConstTy) + }, ); hir::ItemKind::Const(ty, generics, body_id) } @@ -448,8 +451,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ty: &Ty, span: Span, body: Option<&Expr>, + impl_trait_position: ImplTraitPosition, ) -> (&'hir hir::Ty<'hir>, hir::BodyId) { - let ty = self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy)); + let ty = self.lower_ty(ty, &ImplTraitContext::Disallowed(impl_trait_position)); (ty, self.lower_const_body(span, body)) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 998a7322fa01..dc23b1dce7bf 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -322,7 +322,7 @@ impl std::fmt::Display for ImplTraitPosition { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = match self { ImplTraitPosition::Path => "paths", - ImplTraitPosition::Variable => "variable bindings", + ImplTraitPosition::Variable => "the type of variable bindings", ImplTraitPosition::Trait => "traits", ImplTraitPosition::AsyncBlock => "async blocks", ImplTraitPosition::Bound => "bounds", @@ -334,7 +334,7 @@ impl std::fmt::Display for ImplTraitPosition { ImplTraitPosition::ExternFnReturn => "`extern fn` return types", ImplTraitPosition::ClosureReturn => "closure return types", ImplTraitPosition::PointerReturn => "`fn` pointer return types", - ImplTraitPosition::FnTraitReturn => "the return types of `Fn` trait bounds", + ImplTraitPosition::FnTraitReturn => "the return type of `Fn` trait bounds", ImplTraitPosition::GenericDefault => "generic parameter defaults", ImplTraitPosition::ConstTy => "const types", ImplTraitPosition::StaticTy => "static types", diff --git a/tests/ui/associated-consts/issue-105330.stderr b/tests/ui/associated-consts/issue-105330.stderr index aeedf6b19497..e1461fec296e 100644 --- a/tests/ui/associated-consts/issue-105330.stderr +++ b/tests/ui/associated-consts/issue-105330.stderr @@ -33,11 +33,13 @@ LL | fn main>() { = note: see issue #92827 for more information = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in impl headers +error[E0562]: `impl Trait` is not allowed in impl headers --> $DIR/issue-105330.rs:6:27 | LL | impl TraitWAssocConst for impl Demo { | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0131]: `main` function is not allowed to have generic parameters --> $DIR/issue-105330.rs:15:8 diff --git a/tests/ui/feature-gates/feature-gate-associated_type_bounds.rs b/tests/ui/feature-gates/feature-gate-associated_type_bounds.rs index 152c7a8de66a..073599edad7b 100644 --- a/tests/ui/feature-gates/feature-gate-associated_type_bounds.rs +++ b/tests/ui/feature-gates/feature-gate-associated_type_bounds.rs @@ -54,20 +54,20 @@ fn _rpit_dyn() -> Box> { Box::new(S1) } const _cdef: impl Tr1 = S1; //~^ ERROR associated type bounds are unstable -//~| ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~| ERROR `impl Trait` is not allowed in const types // FIXME: uncomment when `impl_trait_in_bindings` feature is fixed. // const _cdef_dyn: &dyn Tr1 = &S1; static _sdef: impl Tr1 = S1; //~^ ERROR associated type bounds are unstable -//~| ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~| ERROR `impl Trait` is not allowed in static types // FIXME: uncomment when `impl_trait_in_bindings` feature is fixed. // static _sdef_dyn: &dyn Tr1 = &S1; fn main() { let _: impl Tr1 = S1; //~^ ERROR associated type bounds are unstable - //~| ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~| ERROR `impl Trait` is not allowed in the type of variable bindings // FIXME: uncomment when `impl_trait_in_bindings` feature is fixed. // let _: &dyn Tr1 = &S1; } diff --git a/tests/ui/feature-gates/feature-gate-associated_type_bounds.stderr b/tests/ui/feature-gates/feature-gate-associated_type_bounds.stderr index f2bceda9bea7..4a643d31259f 100644 --- a/tests/ui/feature-gates/feature-gate-associated_type_bounds.stderr +++ b/tests/ui/feature-gates/feature-gate-associated_type_bounds.stderr @@ -115,23 +115,29 @@ LL | let _: impl Tr1 = S1; = note: see issue #52662 for more information = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in const types +error[E0562]: `impl Trait` is not allowed in const types --> $DIR/feature-gate-associated_type_bounds.rs:55:14 | LL | const _cdef: impl Tr1 = S1; | ^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in const types +error[E0562]: `impl Trait` is not allowed in static types --> $DIR/feature-gate-associated_type_bounds.rs:61:15 | LL | static _sdef: impl Tr1 = S1; | ^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/feature-gate-associated_type_bounds.rs:68:12 | LL | let _: impl Tr1 = S1; | ^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0277]: the trait bound `<::A as Iterator>::Item: Copy` is not satisfied --> $DIR/feature-gate-associated_type_bounds.rs:12:28 diff --git a/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.rs b/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.rs index f07abb9d0496..c75eabd6ac83 100644 --- a/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.rs +++ b/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.rs @@ -1,6 +1,6 @@ fn f() -> impl Fn() -> impl Sized { || () } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds +//~^ ERROR `impl Trait` is not allowed in the return type of `Fn` trait bounds fn g() -> &'static dyn Fn() -> impl Sized { &|| () } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds +//~^ ERROR `impl Trait` is not allowed in the return type of `Fn` trait bounds fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.stderr b/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.stderr index af56e2bd9ef3..dacf1ca4c37c 100644 --- a/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.stderr +++ b/tests/ui/feature-gates/feature-gate-impl_trait_in_fn_trait_return.stderr @@ -1,18 +1,20 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the return type of `Fn` trait bounds --> $DIR/feature-gate-impl_trait_in_fn_trait_return.rs:1:24 | LL | fn f() -> impl Fn() -> impl Sized { || () } | ^^^^^^^^^^ | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods = note: see issue #99697 for more information = help: add `#![feature(impl_trait_in_fn_trait_return)]` to the crate attributes to enable -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the return type of `Fn` trait bounds --> $DIR/feature-gate-impl_trait_in_fn_trait_return.rs:3:32 | LL | fn g() -> &'static dyn Fn() -> impl Sized { &|| () } | ^^^^^^^^^^ | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods = note: see issue #99697 for more information = help: add `#![feature(impl_trait_in_fn_trait_return)]` to the crate attributes to enable diff --git a/tests/ui/impl-trait/issues/issue-54600.rs b/tests/ui/impl-trait/issues/issue-54600.rs index ccf2767012e6..62bfd7cd9688 100644 --- a/tests/ui/impl-trait/issues/issue-54600.rs +++ b/tests/ui/impl-trait/issues/issue-54600.rs @@ -2,6 +2,6 @@ use std::fmt::Debug; fn main() { let x: Option = Some(44_u32); - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in the type of variable bindings println!("{:?}", x); } diff --git a/tests/ui/impl-trait/issues/issue-54600.stderr b/tests/ui/impl-trait/issues/issue-54600.stderr index 946ad74b8722..c75c0fa0f055 100644 --- a/tests/ui/impl-trait/issues/issue-54600.stderr +++ b/tests/ui/impl-trait/issues/issue-54600.stderr @@ -1,8 +1,10 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/issue-54600.rs:4:19 | LL | let x: Option = Some(44_u32); | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-54840.rs b/tests/ui/impl-trait/issues/issue-54840.rs index 910d23f1d938..65257d2f7f1d 100644 --- a/tests/ui/impl-trait/issues/issue-54840.rs +++ b/tests/ui/impl-trait/issues/issue-54840.rs @@ -3,5 +3,5 @@ use std::ops::Add; fn main() { let i: i32 = 0; let j: &impl Add = &i; - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in the type of variable bindings } diff --git a/tests/ui/impl-trait/issues/issue-54840.stderr b/tests/ui/impl-trait/issues/issue-54840.stderr index c4ab79f110dd..de75256d5a93 100644 --- a/tests/ui/impl-trait/issues/issue-54840.stderr +++ b/tests/ui/impl-trait/issues/issue-54840.stderr @@ -1,8 +1,10 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/issue-54840.rs:5:13 | LL | let j: &impl Add = &i; | ^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-58504.rs b/tests/ui/impl-trait/issues/issue-58504.rs index 03b51ae92d18..4f7a35e81b80 100644 --- a/tests/ui/impl-trait/issues/issue-58504.rs +++ b/tests/ui/impl-trait/issues/issue-58504.rs @@ -8,5 +8,5 @@ fn mk_gen() -> impl Coroutine { fn main() { let gens: [impl Coroutine;2] = [ mk_gen(), mk_gen() ]; - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in the type of variable bindings } diff --git a/tests/ui/impl-trait/issues/issue-58504.stderr b/tests/ui/impl-trait/issues/issue-58504.stderr index e67e48728ed0..8231732bba1c 100644 --- a/tests/ui/impl-trait/issues/issue-58504.stderr +++ b/tests/ui/impl-trait/issues/issue-58504.stderr @@ -1,8 +1,10 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/issue-58504.rs:10:16 | LL | let gens: [impl Coroutine;2] = [ mk_gen(), mk_gen() ]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-58956.rs b/tests/ui/impl-trait/issues/issue-58956.rs index 5d5566860c0f..a59de2379d8f 100644 --- a/tests/ui/impl-trait/issues/issue-58956.rs +++ b/tests/ui/impl-trait/issues/issue-58956.rs @@ -5,9 +5,9 @@ impl Lam for B {} pub struct Wrap(T); const _A: impl Lam = { - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in const types let x: Wrap = Wrap(B); - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in the type of variable bindings x.0 }; diff --git a/tests/ui/impl-trait/issues/issue-58956.stderr b/tests/ui/impl-trait/issues/issue-58956.stderr index 5ee33352afa2..0c81c69def3a 100644 --- a/tests/ui/impl-trait/issues/issue-58956.stderr +++ b/tests/ui/impl-trait/issues/issue-58956.stderr @@ -1,14 +1,18 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in const types +error[E0562]: `impl Trait` is not allowed in const types --> $DIR/issue-58956.rs:7:11 | LL | const _A: impl Lam = { | ^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/issue-58956.rs:9:17 | LL | let x: Wrap = Wrap(B); | ^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/issues/issue-70971.rs b/tests/ui/impl-trait/issues/issue-70971.rs index c24259a71eb2..2f2c2e8f441a 100644 --- a/tests/ui/impl-trait/issues/issue-70971.rs +++ b/tests/ui/impl-trait/issues/issue-70971.rs @@ -1,4 +1,4 @@ fn main() { let x : (impl Copy,) = (true,); - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in the type of variable bindings } diff --git a/tests/ui/impl-trait/issues/issue-70971.stderr b/tests/ui/impl-trait/issues/issue-70971.stderr index fcc67291e4d2..28c463cea850 100644 --- a/tests/ui/impl-trait/issues/issue-70971.stderr +++ b/tests/ui/impl-trait/issues/issue-70971.stderr @@ -1,8 +1,10 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/issue-70971.rs:2:14 | LL | let x : (impl Copy,) = (true,); | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-79099.rs b/tests/ui/impl-trait/issues/issue-79099.rs index 22c66491ce98..757e61fb631e 100644 --- a/tests/ui/impl-trait/issues/issue-79099.rs +++ b/tests/ui/impl-trait/issues/issue-79099.rs @@ -1,7 +1,7 @@ struct Bug { V1: [(); { let f: impl core::future::Future = async { 1 }; - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in the type of variable bindings //~| expected identifier 1 }], diff --git a/tests/ui/impl-trait/issues/issue-79099.stderr b/tests/ui/impl-trait/issues/issue-79099.stderr index 82fc03c61b3b..6c26d5bd1b74 100644 --- a/tests/ui/impl-trait/issues/issue-79099.stderr +++ b/tests/ui/impl-trait/issues/issue-79099.stderr @@ -9,11 +9,13 @@ LL | let f: impl core::future::Future = async { 1 }; = help: pass `--edition 2021` to `rustc` = note: for more on editions, read https://doc.rust-lang.org/edition-guide -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/issue-79099.rs:3:16 | LL | let f: impl core::future::Future = async { 1 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.rs b/tests/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.rs index 3224145bffeb..771b29f3c7e1 100644 --- a/tests/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.rs +++ b/tests/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.rs @@ -1,8 +1,8 @@ struct Foo(T); -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in generic parameter defaults type Result = std::result::Result; -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in generic parameter defaults // should not cause ICE fn x() -> Foo { diff --git a/tests/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.stderr b/tests/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.stderr index 56be4577d51a..07d6c5b6b1c8 100644 --- a/tests/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.stderr +++ b/tests/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.stderr @@ -1,14 +1,18 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generic parameter defaults +error[E0562]: `impl Trait` is not allowed in generic parameter defaults --> $DIR/issue-83929-impl-trait-in-generic-default.rs:1:16 | LL | struct Foo(T); | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generic parameter defaults +error[E0562]: `impl Trait` is not allowed in generic parameter defaults --> $DIR/issue-83929-impl-trait-in-generic-default.rs:4:20 | LL | type Result = std::result::Result; | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/issues/issue-84919.rs b/tests/ui/impl-trait/issues/issue-84919.rs index 77d27d7c06b6..0f911ba23ae2 100644 --- a/tests/ui/impl-trait/issues/issue-84919.rs +++ b/tests/ui/impl-trait/issues/issue-84919.rs @@ -3,7 +3,7 @@ impl Trait for () {} fn foo<'a: 'a>() { let _x: impl Trait = (); - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in the type of variable bindings } fn main() {} diff --git a/tests/ui/impl-trait/issues/issue-84919.stderr b/tests/ui/impl-trait/issues/issue-84919.stderr index 963865efa69f..02d2ce28fb3e 100644 --- a/tests/ui/impl-trait/issues/issue-84919.stderr +++ b/tests/ui/impl-trait/issues/issue-84919.stderr @@ -1,8 +1,10 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/issue-84919.rs:5:13 | LL | let _x: impl Trait = (); | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-86642.rs b/tests/ui/impl-trait/issues/issue-86642.rs index 49f8944ac4af..74be8779d445 100644 --- a/tests/ui/impl-trait/issues/issue-86642.rs +++ b/tests/ui/impl-trait/issues/issue-86642.rs @@ -1,5 +1,5 @@ static x: impl Fn(&str) -> Result<&str, ()> = move |source| { - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in static types let res = (move |source| Ok(source))(source); let res = res.or((move |source| Ok(source))(source)); res diff --git a/tests/ui/impl-trait/issues/issue-86642.stderr b/tests/ui/impl-trait/issues/issue-86642.stderr index 3ad18a13290a..19fd5bc0c1ca 100644 --- a/tests/ui/impl-trait/issues/issue-86642.stderr +++ b/tests/ui/impl-trait/issues/issue-86642.stderr @@ -1,8 +1,10 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in const types +error[E0562]: `impl Trait` is not allowed in static types --> $DIR/issue-86642.rs:1:11 | LL | static x: impl Fn(&str) -> Result<&str, ()> = move |source| { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-87295.rs b/tests/ui/impl-trait/issues/issue-87295.rs index eb44020ac0e7..a765e14884b0 100644 --- a/tests/ui/impl-trait/issues/issue-87295.rs +++ b/tests/ui/impl-trait/issues/issue-87295.rs @@ -14,5 +14,5 @@ impl Struct { fn main() { let _do_not_waste: Struct> = Struct::new(()); - //~^ `impl Trait` only allowed in function and inherent method argument and return types + //~^ `impl Trait` is not allowed in the type of variable bindings } diff --git a/tests/ui/impl-trait/issues/issue-87295.stderr b/tests/ui/impl-trait/issues/issue-87295.stderr index e9a635f244bc..78274a056ec6 100644 --- a/tests/ui/impl-trait/issues/issue-87295.stderr +++ b/tests/ui/impl-trait/issues/issue-87295.stderr @@ -1,8 +1,10 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/issue-87295.rs:16:31 | LL | let _do_not_waste: Struct> = Struct::new(()); | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/nested_impl_trait.rs b/tests/ui/impl-trait/nested_impl_trait.rs index c036b9e367a1..760102794c34 100644 --- a/tests/ui/impl-trait/nested_impl_trait.rs +++ b/tests/ui/impl-trait/nested_impl_trait.rs @@ -9,7 +9,7 @@ fn bad_in_ret_position(x: impl Into) -> impl Into { x } fn bad_in_fn_syntax(x: fn() -> impl Into) {} //~^ ERROR nested `impl Trait` is not allowed -//~| `impl Trait` only allowed in function and inherent method argument and return types +//~| `impl Trait` is not allowed in `fn` pointer fn bad_in_arg_position(_: impl Into) { } //~^ ERROR nested `impl Trait` is not allowed diff --git a/tests/ui/impl-trait/nested_impl_trait.stderr b/tests/ui/impl-trait/nested_impl_trait.stderr index f1cafd958b05..31c3e0c90136 100644 --- a/tests/ui/impl-trait/nested_impl_trait.stderr +++ b/tests/ui/impl-trait/nested_impl_trait.stderr @@ -34,11 +34,13 @@ LL | fn bad(x: impl Into) -> impl Into { x } | | nested `impl Trait` here | outer `impl Trait` -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer return types +error[E0562]: `impl Trait` is not allowed in `fn` pointer return types --> $DIR/nested_impl_trait.rs:10:32 | LL | fn bad_in_fn_syntax(x: fn() -> impl Into) {} | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0277]: the trait bound `impl Debug: From>` is not satisfied --> $DIR/nested_impl_trait.rs:6:46 diff --git a/tests/ui/impl-trait/where-allowed.rs b/tests/ui/impl-trait/where-allowed.rs index 158dc5ab9745..5ce63db684f3 100644 --- a/tests/ui/impl-trait/where-allowed.rs +++ b/tests/ui/impl-trait/where-allowed.rs @@ -16,47 +16,47 @@ fn in_adt_in_parameters(_: Vec) { panic!() } // Disallowed fn in_fn_parameter_in_parameters(_: fn(impl Debug)) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in `fn` pointer // Disallowed fn in_fn_return_in_parameters(_: fn() -> impl Debug) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in `fn` pointer // Disallowed fn in_fn_parameter_in_return() -> fn(impl Debug) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in `fn` pointer // Disallowed fn in_fn_return_in_return() -> fn() -> impl Debug { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in `fn` pointer // Disallowed fn in_dyn_Fn_parameter_in_parameters(_: &dyn Fn(impl Debug)) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the parameters of `Fn` trait bounds // Disallowed fn in_dyn_Fn_return_in_parameters(_: &dyn Fn() -> impl Debug) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the return type of `Fn` trait bounds // Disallowed fn in_dyn_Fn_parameter_in_return() -> &'static dyn Fn(impl Debug) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the parameters of `Fn` trait bounds // Allowed fn in_dyn_Fn_return_in_return() -> &'static dyn Fn() -> impl Debug { panic!() } // Disallowed fn in_impl_Fn_parameter_in_parameters(_: &impl Fn(impl Debug)) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the parameters of `Fn` trait bounds //~^^ ERROR nested `impl Trait` is not allowed // Disallowed fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the return type of `Fn` trait bounds // Disallowed fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the parameters of `Fn` trait bounds //~| ERROR nested `impl Trait` is not allowed // Allowed @@ -64,11 +64,11 @@ fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { panic!() // Disallowed fn in_Fn_parameter_in_generics (_: F) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the parameters of `Fn` trait bounds // Disallowed fn in_Fn_return_in_generics impl Debug> (_: F) { panic!() } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the return type of `Fn` trait bounds // Allowed @@ -81,22 +81,22 @@ fn in_impl_Trait_in_return() -> impl IntoIterator { // Disallowed struct InBraceStructField { x: impl Debug } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in field types // Disallowed struct InAdtInBraceStructField { x: Vec } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in field types // Disallowed struct InTupleStructField(impl Debug); -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in field types // Disallowed enum InEnum { InBraceVariant { x: impl Debug }, - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in field types InTupleVariant(impl Debug), - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in field types } // Allowed @@ -136,10 +136,10 @@ impl DummyType { // Disallowed extern "C" { fn in_foreign_parameters(_: impl Debug); - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in `extern fn` fn in_foreign_return() -> impl Debug; - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in `extern fn` } // Allowed @@ -155,97 +155,97 @@ type InTypeAlias = impl Debug; //~^ ERROR `impl Trait` in type aliases is unstable type InReturnInTypeAlias = fn() -> impl Debug; -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in `fn` pointer //~| ERROR `impl Trait` in type aliases is unstable // Disallowed in impl headers impl PartialEq for () { - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in traits } // Disallowed in impl headers impl PartialEq<()> for impl Debug { - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in impl headers } // Disallowed in inherent impls impl impl Debug { - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in impl headers } // Disallowed in inherent impls struct InInherentImplAdt { t: T } impl InInherentImplAdt { - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in impl headers } // Disallowed in where clauses fn in_fn_where_clause() where impl Debug: Debug -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in bounds { } // Disallowed in where clauses fn in_adt_in_fn_where_clause() where Vec: Debug -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in bounds { } // Disallowed fn in_trait_parameter_in_fn_where_clause() where T: PartialEq -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in bounds { } // Disallowed fn in_Fn_parameter_in_fn_where_clause() where T: Fn(impl Debug) -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the parameters of `Fn` trait bounds { } // Disallowed fn in_Fn_return_in_fn_where_clause() where T: Fn() -> impl Debug -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in the return type of `Fn` trait bounds { } // Disallowed struct InStructGenericParamDefault(T); -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in generic parameter defaults // Disallowed enum InEnumGenericParamDefault { Variant(T) } -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in generic parameter defaults // Disallowed trait InTraitGenericParamDefault {} -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in generic parameter defaults // Disallowed type InTypeAliasGenericParamDefault = T; -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in generic parameter defaults // Disallowed impl T {} //~^ ERROR defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions //~| WARNING this was previously accepted by the compiler but is being phased out -//~| ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~| ERROR `impl Trait` is not allowed in generic parameter defaults //~| ERROR no nominal type found // Disallowed fn in_method_generic_param_default(_: T) {} //~^ ERROR defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions //~| WARNING this was previously accepted by the compiler but is being phased out -//~| ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~| ERROR `impl Trait` is not allowed in generic parameter defaults fn main() { let _in_local_variable: impl Fn() = || {}; - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in the type of variable bindings let _in_return_in_local_variable = || -> impl Fn() { || {} }; - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in closure return types } diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index 9c841342ed38..5e8a8637d048 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -43,227 +43,301 @@ LL | type InReturnInTypeAlias = fn() -> impl Debug; = note: see issue #63063 for more information = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer parameters +error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters --> $DIR/where-allowed.rs:18:40 | LL | fn in_fn_parameter_in_parameters(_: fn(impl Debug)) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer return types +error[E0562]: `impl Trait` is not allowed in `fn` pointer return types --> $DIR/where-allowed.rs:22:42 | LL | fn in_fn_return_in_parameters(_: fn() -> impl Debug) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer parameters +error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters --> $DIR/where-allowed.rs:26:38 | LL | fn in_fn_parameter_in_return() -> fn(impl Debug) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer return types +error[E0562]: `impl Trait` is not allowed in `fn` pointer return types --> $DIR/where-allowed.rs:30:40 | LL | fn in_fn_return_in_return() -> fn() -> impl Debug { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:34:49 | LL | fn in_dyn_Fn_parameter_in_parameters(_: &dyn Fn(impl Debug)) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the return type of `Fn` trait bounds --> $DIR/where-allowed.rs:38:51 | LL | fn in_dyn_Fn_return_in_parameters(_: &dyn Fn() -> impl Debug) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:42:55 | LL | fn in_dyn_Fn_parameter_in_return() -> &'static dyn Fn(impl Debug) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:49:51 | LL | fn in_impl_Fn_parameter_in_parameters(_: &impl Fn(impl Debug)) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the return type of `Fn` trait bounds --> $DIR/where-allowed.rs:54:53 | LL | fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:58:57 | LL | fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:66:38 | LL | fn in_Fn_parameter_in_generics (_: F) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the return type of `Fn` trait bounds --> $DIR/where-allowed.rs:70:40 | LL | fn in_Fn_return_in_generics impl Debug> (_: F) { panic!() } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in field types +error[E0562]: `impl Trait` is not allowed in field types --> $DIR/where-allowed.rs:83:32 | LL | struct InBraceStructField { x: impl Debug } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in field types +error[E0562]: `impl Trait` is not allowed in field types --> $DIR/where-allowed.rs:87:41 | LL | struct InAdtInBraceStructField { x: Vec } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in field types +error[E0562]: `impl Trait` is not allowed in field types --> $DIR/where-allowed.rs:91:27 | LL | struct InTupleStructField(impl Debug); | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in field types +error[E0562]: `impl Trait` is not allowed in field types --> $DIR/where-allowed.rs:96:25 | LL | InBraceVariant { x: impl Debug }, | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in field types +error[E0562]: `impl Trait` is not allowed in field types --> $DIR/where-allowed.rs:98:20 | LL | InTupleVariant(impl Debug), | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `extern fn` parameters +error[E0562]: `impl Trait` is not allowed in `extern fn` parameters --> $DIR/where-allowed.rs:138:33 | LL | fn in_foreign_parameters(_: impl Debug); | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `extern fn` return types +error[E0562]: `impl Trait` is not allowed in `extern fn` return types --> $DIR/where-allowed.rs:141:31 | LL | fn in_foreign_return() -> impl Debug; | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer return types +error[E0562]: `impl Trait` is not allowed in `fn` pointer return types --> $DIR/where-allowed.rs:157:39 | LL | type InReturnInTypeAlias = fn() -> impl Debug; | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in traits +error[E0562]: `impl Trait` is not allowed in traits --> $DIR/where-allowed.rs:162:16 | LL | impl PartialEq for () { | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in impl headers +error[E0562]: `impl Trait` is not allowed in impl headers --> $DIR/where-allowed.rs:167:24 | LL | impl PartialEq<()> for impl Debug { | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in impl headers +error[E0562]: `impl Trait` is not allowed in impl headers --> $DIR/where-allowed.rs:172:6 | LL | impl impl Debug { | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in impl headers +error[E0562]: `impl Trait` is not allowed in impl headers --> $DIR/where-allowed.rs:178:24 | LL | impl InInherentImplAdt { | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in bounds +error[E0562]: `impl Trait` is not allowed in bounds --> $DIR/where-allowed.rs:184:11 | LL | where impl Debug: Debug | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in bounds +error[E0562]: `impl Trait` is not allowed in bounds --> $DIR/where-allowed.rs:191:15 | LL | where Vec: Debug | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in bounds +error[E0562]: `impl Trait` is not allowed in bounds --> $DIR/where-allowed.rs:198:24 | LL | where T: PartialEq | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the parameters of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the parameters of `Fn` trait bounds --> $DIR/where-allowed.rs:205:17 | LL | where T: Fn(impl Debug) | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in the return types of `Fn` trait bounds +error[E0562]: `impl Trait` is not allowed in the return type of `Fn` trait bounds --> $DIR/where-allowed.rs:212:22 | LL | where T: Fn() -> impl Debug | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generic parameter defaults +error[E0562]: `impl Trait` is not allowed in generic parameter defaults --> $DIR/where-allowed.rs:218:40 | LL | struct InStructGenericParamDefault(T); | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generic parameter defaults +error[E0562]: `impl Trait` is not allowed in generic parameter defaults --> $DIR/where-allowed.rs:222:36 | LL | enum InEnumGenericParamDefault { Variant(T) } | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generic parameter defaults +error[E0562]: `impl Trait` is not allowed in generic parameter defaults --> $DIR/where-allowed.rs:226:38 | LL | trait InTraitGenericParamDefault {} | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generic parameter defaults +error[E0562]: `impl Trait` is not allowed in generic parameter defaults --> $DIR/where-allowed.rs:230:41 | LL | type InTypeAliasGenericParamDefault = T; | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generic parameter defaults +error[E0562]: `impl Trait` is not allowed in generic parameter defaults --> $DIR/where-allowed.rs:234:11 | LL | impl T {} | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generic parameter defaults +error[E0562]: `impl Trait` is not allowed in generic parameter defaults --> $DIR/where-allowed.rs:241:40 | LL | fn in_method_generic_param_default(_: T) {} | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/where-allowed.rs:247:29 | LL | let _in_local_variable: impl Fn() = || {}; | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in closure return types +error[E0562]: `impl Trait` is not allowed in closure return types --> $DIR/where-allowed.rs:249:46 | LL | let _in_return_in_local_variable = || -> impl Fn() { || {} }; | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions --> $DIR/where-allowed.rs:234:7 diff --git a/tests/ui/issues/issue-47715.rs b/tests/ui/issues/issue-47715.rs index 0a770593bc9c..bf2b03351b29 100644 --- a/tests/ui/issues/issue-47715.rs +++ b/tests/ui/issues/issue-47715.rs @@ -7,22 +7,22 @@ trait Iterable { } struct Container> { - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in generics field: T } enum Enum> { - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in generics A(T), } union Union + Copy> { - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in generics x: T, } type Type> = T; -//~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR `impl Trait` is not allowed in generics fn main() { } diff --git a/tests/ui/issues/issue-47715.stderr b/tests/ui/issues/issue-47715.stderr index 2ded98781c64..8ed9ff439521 100644 --- a/tests/ui/issues/issue-47715.stderr +++ b/tests/ui/issues/issue-47715.stderr @@ -1,26 +1,34 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generics +error[E0562]: `impl Trait` is not allowed in generics --> $DIR/issue-47715.rs:9:37 | LL | struct Container> { | ^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generics +error[E0562]: `impl Trait` is not allowed in generics --> $DIR/issue-47715.rs:14:30 | LL | enum Enum> { | ^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generics +error[E0562]: `impl Trait` is not allowed in generics --> $DIR/issue-47715.rs:19:32 | LL | union Union + Copy> { | ^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in generics +error[E0562]: `impl Trait` is not allowed in generics --> $DIR/issue-47715.rs:24:30 | LL | type Type> = T; | ^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 4 previous errors diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.rs index 9796823a7244..46c2c22cac1c 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.rs @@ -4,7 +4,7 @@ // FIXME: this is ruled out for now but should work type Foo = fn() -> impl Send; -//~^ ERROR: `impl Trait` only allowed in function and inherent method argument and return types +//~^ ERROR: `impl Trait` is not allowed in `fn` pointer return types fn make_foo() -> Foo { || 15 diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.stderr index 5641ff301649..0f73c1424704 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.stderr +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.stderr @@ -1,8 +1,10 @@ -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `fn` pointer return types +error[E0562]: `impl Trait` is not allowed in `fn` pointer return types --> $DIR/type-alias-impl-trait-fn-type.rs:6:20 | LL | type Foo = fn() -> impl Send; | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-104513-ice.rs b/tests/ui/typeck/issue-104513-ice.rs index 4968d3f51fe8..aaeee9cef48a 100644 --- a/tests/ui/typeck/issue-104513-ice.rs +++ b/tests/ui/typeck/issue-104513-ice.rs @@ -1,6 +1,6 @@ struct S; fn f() { let _: S = S; //~ ERROR cannot find trait `Oops` in this scope - //~^ ERROR `impl Trait` only allowed in function and inherent method argument and return types + //~^ ERROR `impl Trait` is not allowed in the type of variable bindings } fn main() {} diff --git a/tests/ui/typeck/issue-104513-ice.stderr b/tests/ui/typeck/issue-104513-ice.stderr index 56c6b3361546..37d38a76a403 100644 --- a/tests/ui/typeck/issue-104513-ice.stderr +++ b/tests/ui/typeck/issue-104513-ice.stderr @@ -4,11 +4,13 @@ error[E0405]: cannot find trait `Oops` in this scope LL | let _: S = S; | ^^^^ not found in this scope -error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in variable bindings +error[E0562]: `impl Trait` is not allowed in the type of variable bindings --> $DIR/issue-104513-ice.rs:3:14 | LL | let _: S = S; | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 2 previous errors From 68bb76634d916a5ea692f0a6bfae359e7ff556ab Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 7 Jan 2024 18:59:20 +0000 Subject: [PATCH 194/257] Unions are not PointerLike --- compiler/rustc_target/src/abi/mod.rs | 4 ++-- tests/ui/dyn-star/union.rs | 16 ++++++++++++++++ tests/ui/dyn-star/union.stderr | 20 ++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/ui/dyn-star/union.rs create mode 100644 tests/ui/dyn-star/union.stderr diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index a274790bffcf..24e49ff648f2 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -120,11 +120,11 @@ impl<'a> Layout<'a> { /// Whether the layout is from a type that implements [`std::marker::PointerLike`]. /// /// Currently, that means that the type is pointer-sized, pointer-aligned, - /// and has a scalar ABI. + /// and has a initialized (non-union), scalar ABI. pub fn is_pointer_like(self, data_layout: &TargetDataLayout) -> bool { self.size() == data_layout.pointer_size && self.align().abi == data_layout.pointer_align.abi - && matches!(self.abi(), Abi::Scalar(..)) + && matches!(self.abi(), Abi::Scalar(Scalar::Initialized { .. })) } } diff --git a/tests/ui/dyn-star/union.rs b/tests/ui/dyn-star/union.rs new file mode 100644 index 000000000000..ad3a85a937ae --- /dev/null +++ b/tests/ui/dyn-star/union.rs @@ -0,0 +1,16 @@ +#![feature(dyn_star)] +//~^ WARN the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes + +union Union { + x: usize, +} + +trait Trait {} +impl Trait for Union {} + +fn bar(_: dyn* Trait) {} + +fn main() { + bar(Union { x: 0usize }); + //~^ ERROR `Union` needs to have the same ABI as a pointer +} diff --git a/tests/ui/dyn-star/union.stderr b/tests/ui/dyn-star/union.stderr new file mode 100644 index 000000000000..906eb4f5163e --- /dev/null +++ b/tests/ui/dyn-star/union.stderr @@ -0,0 +1,20 @@ +warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/union.rs:1:12 + | +LL | #![feature(dyn_star)] + | ^^^^^^^^ + | + = note: see issue #102425 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: `Union` needs to have the same ABI as a pointer + --> $DIR/union.rs:14:9 + | +LL | bar(Union { x: 0usize }); + | ^^^^^^^^^^^^^^^^^^^ `Union` needs to be a pointer-like type + | + = help: the trait `PointerLike` is not implemented for `Union` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. From 5b30586ba8905a1a14d5d05522c697ab1b0800f7 Mon Sep 17 00:00:00 2001 From: Mads Ravn Date: Sun, 7 Jan 2024 20:39:46 +0100 Subject: [PATCH 195/257] Adding alignment to the list of cases to test for specific error message. Covers `>`, `^` and `<`. --- compiler/rustc_parse_format/src/lib.rs | 28 +++++++++++++++---- tests/ui/fmt/format-string-wrong-order.rs | 6 ++++ tests/ui/fmt/format-string-wrong-order.stderr | 26 ++++++++++++++++- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index e886db3da296..eff54ac93089 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -289,10 +289,10 @@ impl<'a> Iterator for Parser<'a> { } } else { if let Some(&(_, maybe)) = self.cur.peek() { - if maybe == '?' { - self.suggest_format(); - } else { - self.suggest_positional_arg_instead_of_captured_arg(arg); + match maybe { + '?' => self.suggest_format_debug(), + '<' | '^' | '>' => self.suggest_format_align(maybe), + _ => self.suggest_positional_arg_instead_of_captured_arg(arg), } } } @@ -868,10 +868,9 @@ impl<'a> Parser<'a> { found.then_some(cur) } - fn suggest_format(&mut self) { + fn suggest_format_debug(&mut self) { if let (Some(pos), Some(_)) = (self.consume_pos('?'), self.consume_pos(':')) { let word = self.word(); - let _end = self.current_pos(); let pos = self.to_span_index(pos); self.errors.insert( 0, @@ -887,6 +886,23 @@ impl<'a> Parser<'a> { } } + fn suggest_format_align(&mut self, alignment: char) { + if let Some(pos) = self.consume_pos(alignment) { + let pos = self.to_span_index(pos); + self.errors.insert( + 0, + ParseError { + description: "expected format parameter to occur after `:`".to_owned(), + note: Some(format!("`{}` comes after `:`.", alignment)), + label: format!("expected `{}` to occur after `:`", alignment).to_owned(), + span: pos.to(pos), + secondary_label: None, + suggestion: Suggestion::None, + }, + ); + } + } + fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: Argument<'a>) { if let Some(end) = self.consume_pos('.') { let byte_pos = self.to_span_index(end); diff --git a/tests/ui/fmt/format-string-wrong-order.rs b/tests/ui/fmt/format-string-wrong-order.rs index 0bad54023965..da775be3ffd6 100644 --- a/tests/ui/fmt/format-string-wrong-order.rs +++ b/tests/ui/fmt/format-string-wrong-order.rs @@ -12,4 +12,10 @@ fn main() { //~^ ERROR invalid format string: expected `'}'`, found `'?'` format!("{?:#?}", bar); //~^ ERROR invalid format string: expected format parameter to occur after `:` + format!("Hello {<5:}!", "x"); + //~^ ERROR invalid format string: expected format parameter to occur after `:` + format!("Hello {^5:}!", "x"); + //~^ ERROR invalid format string: expected format parameter to occur after `:` + format!("Hello {>5:}!", "x"); + //~^ ERROR invalid format string: expected format parameter to occur after `:` } diff --git a/tests/ui/fmt/format-string-wrong-order.stderr b/tests/ui/fmt/format-string-wrong-order.stderr index 0a2e04026d97..221228012d6a 100644 --- a/tests/ui/fmt/format-string-wrong-order.stderr +++ b/tests/ui/fmt/format-string-wrong-order.stderr @@ -50,5 +50,29 @@ LL | format!("{?:#?}", bar); | = note: `?` comes after `:`, try `:?` instead -error: aborting due to 6 previous errors +error: invalid format string: expected format parameter to occur after `:` + --> $DIR/format-string-wrong-order.rs:15:21 + | +LL | format!("Hello {<5:}!", "x"); + | ^ expected `<` to occur after `:` in format string + | + = note: `<` comes after `:`. + +error: invalid format string: expected format parameter to occur after `:` + --> $DIR/format-string-wrong-order.rs:17:21 + | +LL | format!("Hello {^5:}!", "x"); + | ^ expected `^` to occur after `:` in format string + | + = note: `^` comes after `:`. + +error: invalid format string: expected format parameter to occur after `:` + --> $DIR/format-string-wrong-order.rs:19:21 + | +LL | format!("Hello {>5:}!", "x"); + | ^ expected `>` to occur after `:` in format string + | + = note: `>` comes after `:`. + +error: aborting due to 9 previous errors From 5be2a85351fcf2a71831938022f5afc96c76f284 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 7 Jan 2024 20:48:31 +0100 Subject: [PATCH 196/257] Delete unused makefile in tests/ui ?????????? --- src/tools/tidy/src/ui_tests.rs | 1 - tests/ui/unused-crate-deps/test.mk | 7 ------- 2 files changed, 8 deletions(-) delete mode 100644 tests/ui/unused-crate-deps/test.mk diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index b4745d4883c5..99c5dce347a0 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -33,7 +33,6 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[ "tests/ui/macros/macro-expanded-include/file.txt", // testing including data with the include macros "tests/ui/macros/not-utf8.bin", // testing including data with the include macros "tests/ui/macros/syntax-extension-source-utils-files/includeme.fragment", // more include - "tests/ui/unused-crate-deps/test.mk", // why would you use make "tests/ui/proc-macro/auxiliary/included-file.txt", // more include "tests/ui/invalid/foo.natvis.xml", // sample debugger visualizer ]; diff --git a/tests/ui/unused-crate-deps/test.mk b/tests/ui/unused-crate-deps/test.mk deleted file mode 100644 index 0b98b4e44fb2..000000000000 --- a/tests/ui/unused-crate-deps/test.mk +++ /dev/null @@ -1,7 +0,0 @@ -# Everyone uses make for building Rust - -foo: bar.rlib - $(RUSTC) --crate-type bin --extern bar=bar.rlib - -%.rlib: %.rs - $(RUSTC) --crate-type lib $< From edec91d624f41c25b3c75bb3df818dc081193dce Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 7 Jan 2024 17:26:29 +0300 Subject: [PATCH 197/257] macro_rules: Add an expansion-local cache to span marker --- compiler/rustc_expand/src/mbe/transcribe.rs | 17 +++++++++++++---- compiler/rustc_span/src/hygiene.rs | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index c969ca7ef89b..434891ebc76e 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -13,19 +13,28 @@ use rustc_errors::DiagnosticBuilder; use rustc_errors::{pluralize, PResult}; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; -use rustc_span::Span; +use rustc_span::{Span, SyntaxContext}; use smallvec::{smallvec, SmallVec}; use std::mem; // A Marker adds the given mark to the syntax context. -struct Marker(LocalExpnId, Transparency); +struct Marker(LocalExpnId, Transparency, FxHashMap); impl MutVisitor for Marker { const VISIT_TOKENS: bool = true; fn visit_span(&mut self, span: &mut Span) { - *span = span.apply_mark(self.0.to_expn_id(), self.1) + // `apply_mark` is a relatively expensive operation, both due to taking hygiene lock, and + // by itself. All tokens in a macro body typically have the same syntactic context, unless + // it's some advanced case with macro-generated macros. So if we cache the marked version + // of that context once, we'll typically have a 100% cache hit rate after that. + let Marker(expn_id, transparency, ref mut cache) = *self; + let data = span.data(); + let marked_ctxt = *cache + .entry(data.ctxt) + .or_insert_with(|| data.ctxt.apply_mark(expn_id.to_expn_id(), transparency)); + *span = data.with_ctxt(marked_ctxt); } } @@ -123,7 +132,7 @@ pub(super) fn transcribe<'a>( // again, and we are done transcribing. let mut result: Vec = Vec::new(); let mut result_stack = Vec::new(); - let mut marker = Marker(cx.current_expansion.id, transparency); + let mut marker = Marker(cx.current_expansion.id, transparency, Default::default()); loop { // Look at the last frame on the stack. diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 08fb1d1345df..6a15961ee20f 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -658,7 +658,7 @@ impl SyntaxContext { } /// Extend a syntax context with a given expansion and transparency. - pub(crate) fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { + pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) } From be0293f468b7be1c2fe0128802584d364f36d244 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 7 Jan 2024 19:13:04 -0500 Subject: [PATCH 198/257] Remove crossbeam-channel The standard library's std::sync::mpsc basically is a crossbeam channel, and for the use case here will definitely suffice. This drops this dependency from librustc_driver. --- Cargo.lock | 1 - compiler/rustc_expand/Cargo.toml | 1 - compiler/rustc_expand/src/proc_macro.rs | 16 ++++++++-------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1948e6c2e5ef..5093b097fc3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3797,7 +3797,6 @@ dependencies = [ name = "rustc_expand" version = "0.0.0" dependencies = [ - "crossbeam-channel", "rustc_ast", "rustc_ast_passes", "rustc_ast_pretty", diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 9189a501aa5f..63247f9d0519 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -9,7 +9,6 @@ doctest = false [dependencies] # tidy-alphabetical-start -crossbeam-channel = "0.5.0" rustc_ast = { path = "../rustc_ast" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 73a7d433b5c6..448e7bfd7328 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -13,16 +13,16 @@ use rustc_session::config::ProcMacroExecutionStrategy; use rustc_span::profiling::SpannedEventArgRecorder; use rustc_span::{Span, DUMMY_SP}; -struct CrossbeamMessagePipe { - tx: crossbeam_channel::Sender, - rx: crossbeam_channel::Receiver, +struct MessagePipe { + tx: std::sync::mpsc::SyncSender, + rx: std::sync::mpsc::Receiver, } -impl pm::bridge::server::MessagePipe for CrossbeamMessagePipe { +impl pm::bridge::server::MessagePipe for MessagePipe { fn new() -> (Self, Self) { - let (tx1, rx1) = crossbeam_channel::bounded(1); - let (tx2, rx2) = crossbeam_channel::bounded(1); - (CrossbeamMessagePipe { tx: tx1, rx: rx2 }, CrossbeamMessagePipe { tx: tx2, rx: rx1 }) + let (tx1, rx1) = std::sync::mpsc::sync_channel(1); + let (tx2, rx2) = std::sync::mpsc::sync_channel(1); + (MessagePipe { tx: tx1, rx: rx2 }, MessagePipe { tx: tx2, rx: rx1 }) } fn send(&mut self, value: T) { @@ -35,7 +35,7 @@ impl pm::bridge::server::MessagePipe for CrossbeamMessagePipe { } fn exec_strategy(ecx: &ExtCtxt<'_>) -> impl pm::bridge::server::ExecutionStrategy { - pm::bridge::server::MaybeCrossThread::>::new( + pm::bridge::server::MaybeCrossThread::>::new( ecx.sess.opts.unstable_opts.proc_macro_execution_strategy == ProcMacroExecutionStrategy::CrossThread, ) From ca2fc426a9b30a4fd4442512cec312ce8f8ce870 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 5 Jan 2024 17:35:54 +1100 Subject: [PATCH 199/257] Remove `Clone` impl for `DiagnosticBuilder`. It seems like a bad idea, just asking for diagnostics to be emitted multiple times. --- compiler/rustc_errors/src/diagnostic_builder.rs | 5 ++++- compiler/rustc_errors/src/lib.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index e018c14a4a5c..fab5affbad1e 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -42,7 +42,6 @@ where /// access in the methods of `DiagnosticBuilder` here, consider /// extending `DiagCtxtFlags`. #[must_use] -#[derive(Clone)] pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { state: DiagnosticBuilderState<'a>, @@ -55,6 +54,10 @@ pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { _marker: PhantomData, } +// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted +// twice, which would be bad. +impl !Clone for DiagnosticBuilder<'_, G> {} + #[derive(Clone)] enum DiagnosticBuilderState<'a> { /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`. diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 25bec9f766b0..e07a9509383b 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -10,6 +10,7 @@ #![feature(extract_if)] #![feature(if_let_guard)] #![feature(let_chains)] +#![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] #![feature(yeet_expr)] From e651f6f029c138004583028df3961e1b24636a07 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 8 Jan 2024 01:00:31 +0000 Subject: [PATCH 200/257] Add helper for when we want to know if an item has a host param --- .../src/transform/check_consts/qualifs.rs | 4 +--- compiler/rustc_hir_typeck/src/callee.rs | 8 +++----- compiler/rustc_middle/src/ty/util.rs | 9 ++++++++- compiler/rustc_trait_selection/src/traits/util.rs | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 7e1cbfe66674..1efa52df5817 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -157,9 +157,7 @@ impl Qualif for NeedsNonConstDrop { // FIXME(effects): If `destruct` is not a `const_trait`, // or effects are disabled in this crate, then give up. let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span)); - if cx.tcx.generics_of(destruct_def_id).host_effect_index.is_none() - || !cx.tcx.features().effects - { + if !cx.tcx.has_host_param(destruct_def_id) || !cx.tcx.features().effects { return NeedsDrop::in_any_value_of_ty(cx, ty); } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index de2cb5a6d5c7..86e76223f5cd 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -490,11 +490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span)); let fn_once_output_def_id = self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span)); - if self.tcx.generics_of(fn_once_def_id).host_effect_index.is_none() { - if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { - self.dcx().emit_err(errors::ConstSelectMustBeConst { span }); - } - } else { + if self.tcx.has_host_param(fn_once_def_id) { let const_param: ty::GenericArg<'tcx> = ([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into(); self.register_predicate(traits::Obligation::new( @@ -523,6 +519,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); self.select_obligations_where_possible(|_| {}); + } else if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { + self.dcx().emit_err(errors::ConstSelectMustBeConst { span }); } } else { self.dcx().emit_err(errors::ConstSelectMustBeFn { span, ty: arg_ty }); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 3a6fbaec9fd4..c46e43a79785 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1,7 +1,7 @@ //! Miscellaneous type-system utilities that are too small to deserve their own modules. use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::query::Providers; +use crate::query::{IntoQueryParam, Providers}; use crate::ty::layout::IntegerExt; use crate::ty::{ self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, @@ -786,6 +786,13 @@ impl<'tcx> TyCtxt<'tcx> { || self.extern_crate(key.as_def_id()).is_some_and(|e| e.is_direct()) } + /// Whether the item has a host effect param. This is different from `TyCtxt::is_const`, + /// because the item must also be "maybe const", and the crate where the item is + /// defined must also have the effects feature enabled. + pub fn has_host_param(self, def_id: impl IntoQueryParam) -> bool { + self.generics_of(def_id).host_effect_index.is_some() + } + pub fn expected_host_effect_param_for_body(self, def_id: impl Into) -> ty::Const<'tcx> { let def_id = def_id.into(); // FIXME(effects): This is suspicious and should probably not be done, diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 19eae93df9c8..c40ed10e52ff 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -271,7 +271,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>( TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()), }; - let trait_ref = if tcx.generics_of(fn_trait_def_id).host_effect_index.is_some() { + let trait_ref = if tcx.has_host_param(fn_trait_def_id) { ty::TraitRef::new( tcx, fn_trait_def_id, From b1b9278851a9512a0c934c12f9c1800169c336f7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 3 Jan 2024 12:17:35 +1100 Subject: [PATCH 201/257] Make `DiagnosticBuilder::emit` consuming. This works for most of its call sites. This is nice, because `emit` very much makes sense as a consuming operation -- indeed, `DiagnosticBuilderState` exists to ensure no diagnostic is emitted twice, but it uses runtime checks. For the small number of call sites where a consuming emit doesn't work, the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will be removed in subsequent commits.) Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes consuming, while `delay_as_bug_without_consuming` is added (which will also be removed in subsequent commits.) All this requires significant changes to `DiagnosticBuilder`'s chaining methods. Currently `DiagnosticBuilder` method chaining uses a non-consuming `&mut self -> &mut Self` style, which allows chaining to be used when the chain ends in `emit()`, like so: ``` struct_err(msg).span(span).emit(); ``` But it doesn't work when producing a `DiagnosticBuilder` value, requiring this: ``` let mut err = self.struct_err(msg); err.span(span); err ``` This style of chaining won't work with consuming `emit` though. For that, we need to use to a `self -> Self` style. That also would allow `DiagnosticBuilder` production to be chained, e.g.: ``` self.struct_err(msg).span(span) ``` However, removing the `&mut self -> &mut Self` style would require that individual modifications of a `DiagnosticBuilder` go from this: ``` err.span(span); ``` to this: ``` err = err.span(span); ``` There are *many* such places. I have a high tolerance for tedious refactorings, but even I gave up after a long time trying to convert them all. Instead, this commit has it both ways: the existing `&mut self -> Self` chaining methods are kept, and new `self -> Self` chaining methods are added, all of which have a `_mv` suffix (short for "move"). Changes to the existing `forward!` macro lets this happen with very little additional boilerplate code. I chose to add the suffix to the new chaining methods rather than the existing ones, because the number of changes required is much smaller that way. This doubled chainging is a bit clumsy, but I think it is worthwhile because it allows a *lot* of good things to subsequently happen. In this commit, there are many `mut` qualifiers removed in places where diagnostics are emitted without being modified. In subsequent commits: - chaining can be used more, making the code more concise; - more use of chaining also permits the removal of redundant diagnostic APIs like `struct_err_with_code`, which can be replaced easily with `struct_err` + `code_mv`; - `emit_without_diagnostic` can be removed, which simplifies a lot of machinery, removing the need for `DiagnosticBuilderState`. --- compiler/rustc_ast_passes/src/feature_gate.rs | 2 +- .../src/diagnostics/region_errors.rs | 2 +- .../src/region_infer/opaque_types.rs | 2 +- compiler/rustc_builtin_macros/src/asm.rs | 6 +- compiler/rustc_builtin_macros/src/assert.rs | 2 +- compiler/rustc_builtin_macros/src/cfg.rs | 2 +- compiler/rustc_builtin_macros/src/cfg_eval.rs | 2 +- .../rustc_builtin_macros/src/cmdline_attrs.rs | 2 +- compiler/rustc_builtin_macros/src/format.rs | 7 +- .../src/proc_macro_harness.rs | 2 +- .../rustc_builtin_macros/src/source_util.rs | 8 +- compiler/rustc_builtin_macros/src/test.rs | 6 +- .../rustc_builtin_macros/src/type_ascribe.rs | 2 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 4 +- .../rustc_codegen_ssa/src/target_features.rs | 2 +- .../src/transform/check_consts/check.rs | 2 +- compiler/rustc_driver_impl/src/lib.rs | 2 +- .../rustc_errors/src/diagnostic_builder.rs | 201 ++++++++++-------- compiler/rustc_expand/src/base.rs | 4 +- compiler/rustc_expand/src/config.rs | 2 +- compiler/rustc_expand/src/expand.rs | 2 +- compiler/rustc_expand/src/mbe/macro_parser.rs | 4 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 2 +- compiler/rustc_expand/src/mbe/quoted.rs | 4 +- compiler/rustc_expand/src/module.rs | 2 +- compiler/rustc_expand/src/proc_macro.rs | 2 +- .../rustc_expand/src/proc_macro_server.rs | 2 +- .../rustc_hir_analysis/src/astconv/bounds.rs | 2 +- .../rustc_hir_analysis/src/astconv/errors.rs | 6 +- .../src/astconv/generics.rs | 2 +- .../rustc_hir_analysis/src/astconv/mod.rs | 4 +- .../src/astconv/object_safety.rs | 2 +- .../rustc_hir_analysis/src/check/check.rs | 22 +- .../src/check/compare_impl_item.rs | 6 +- .../rustc_hir_analysis/src/check/dropck.rs | 4 +- .../rustc_hir_analysis/src/check/intrinsic.rs | 2 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 10 +- .../rustc_hir_analysis/src/coherence/mod.rs | 2 +- .../src/coherence/unsafety.rs | 10 +- .../src/collect/resolve_bound_vars.rs | 2 +- compiler/rustc_hir_typeck/src/callee.rs | 2 +- compiler/rustc_hir_typeck/src/cast.rs | 6 +- compiler/rustc_hir_typeck/src/demand.rs | 6 +- compiler/rustc_hir_typeck/src/expr.rs | 4 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 6 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 5 +- compiler/rustc_hir_typeck/src/intrinsicck.rs | 10 +- compiler/rustc_hir_typeck/src/lib.rs | 8 +- compiler/rustc_hir_typeck/src/op.rs | 2 +- compiler/rustc_hir_typeck/src/pat.rs | 30 ++- .../src/infer/error_reporting/mod.rs | 2 +- .../error_reporting/nice_region_error/mod.rs | 2 +- .../src/infer/error_reporting/note.rs | 2 +- compiler/rustc_interface/src/queries.rs | 2 +- compiler/rustc_middle/src/ty/opaque_types.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 2 +- compiler/rustc_parse/src/lexer/tokentrees.rs | 2 +- compiler/rustc_parse/src/lib.rs | 6 +- .../rustc_parse/src/parser/diagnostics.rs | 10 +- compiler/rustc_parse/src/parser/expr.rs | 17 +- compiler/rustc_parse/src/parser/generics.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 35 +-- compiler/rustc_parse/src/parser/mod.rs | 6 +- compiler/rustc_parse/src/parser/pat.rs | 14 +- compiler/rustc_parse/src/parser/path.rs | 4 +- compiler/rustc_parse/src/parser/stmt.rs | 6 +- compiler/rustc_parse/src/parser/ty.rs | 21 +- compiler/rustc_parse/src/validate_attr.rs | 6 +- .../rustc_passes/src/debugger_visualizer.rs | 2 +- compiler/rustc_passes/src/errors.rs | 2 +- .../rustc_query_system/src/query/plumbing.rs | 2 +- .../rustc_resolve/src/build_reduced_graph.rs | 9 +- compiler/rustc_resolve/src/imports.rs | 2 +- compiler/rustc_resolve/src/late.rs | 4 +- compiler/rustc_resolve/src/macros.rs | 6 +- compiler/rustc_session/src/session.rs | 5 +- .../src/traits/const_evaluatable.rs | 4 +- .../src/traits/error_reporting/suggestions.rs | 2 +- .../src/traits/select/candidate_assembly.rs | 5 +- .../src/traits/specialize/mod.rs | 2 +- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/config.rs | 10 +- .../passes/collect_intra_doc_links.rs | 2 +- src/tools/clippy/clippy_config/src/msrvs.rs | 2 +- src/tools/clippy/clippy_utils/src/attrs.rs | 2 +- src/tools/rustfmt/src/parse/parser.rs | 4 +- 86 files changed, 329 insertions(+), 312 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 2b746789a769..35b8de64af7e 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -23,7 +23,7 @@ macro_rules! gate { ($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{ if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) { feature_err(&$visitor.sess.parse_sess, sym::$feature, $span, $explain) - .help($help) + .help_mv($help) .emit(); } }}; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index b3450b09cdf9..020429c19144 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -348,7 +348,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty); let named_key = self.regioncx.name_regions(self.infcx.tcx, key); let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); - let mut diag = unexpected_hidden_region_diagnostic( + let diag = unexpected_hidden_region_diagnostic( self.infcx.tcx, span, named_ty, diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 093017ecba23..5f427a5ac801 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -421,7 +421,7 @@ fn check_opaque_type_parameter_valid( return Err(tcx .dcx() .struct_span_err(span, "non-defining opaque type use in defining scope") - .span_note(spans, format!("{descr} used multiple times")) + .span_note_mv(spans, format!("{descr} used multiple times")) .emit()); } } diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index b5801c1b0f16..ac90f4d4084d 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -458,7 +458,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option template_part, Err(err) => { - if let Some((mut err, _)) = err { + if let Some((err, _)) = err { err.emit(); } return None; @@ -747,7 +747,7 @@ pub(super) fn expand_asm<'cx>( }; MacEager::expr(expr) } - Err(mut err) => { + Err(err) => { err.emit(); DummyResult::any(sp) } @@ -779,7 +779,7 @@ pub(super) fn expand_global_asm<'cx>( DummyResult::any(sp) } } - Err(mut err) => { + Err(err) => { err.emit(); DummyResult::any(sp) } diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 8fa3fe5b3e6c..501d557f3ab8 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -22,7 +22,7 @@ pub fn expand_assert<'cx>( ) -> Box { let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) { Ok(assert) => assert, - Err(mut err) => { + Err(err) => { err.emit(); return DummyResult::any(span); } diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 48be680b619f..1bc2512a7b05 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -28,7 +28,7 @@ pub fn expand_cfg( ); MacEager::expr(cx.expr_bool(sp, matches_cfg)) } - Err(mut err) => { + Err(err) => { err.emit(); DummyResult::any(sp) } diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index ca26b7ed8270..cfa94b0e7808 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -200,7 +200,7 @@ impl CfgEval<'_, '_> { parser.capture_cfg = true; match parse_annotatable_with(&mut parser) { Ok(a) => annotatable = a, - Err(mut err) => { + Err(err) => { err.emit(); return Some(annotatable); } diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index 2803ddefba21..d956c096d24a 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -18,7 +18,7 @@ pub fn inject(krate: &mut ast::Crate, parse_sess: &ParseSess, attrs: &[String]) let start_span = parser.token.span; let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) { Ok(ai) => ai, - Err(mut err) => { + Err(err) => { err.emit(); continue; } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index a668db438eb2..93381b69fdcc 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -99,7 +99,7 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult< } match p.expect(&token::Comma) { - Err(mut err) => { + Err(err) => { match token::TokenKind::Comma.similar_tokens() { Some(tks) if tks.contains(&p.token.kind) => { // If a similar token is found, then it may be a typo. We @@ -630,8 +630,7 @@ fn report_missing_placeholders( .collect::>(); if !placeholders.is_empty() { - if let Some(mut new_diag) = report_redundant_format_arguments(ecx, args, used, placeholders) - { + if let Some(new_diag) = report_redundant_format_arguments(ecx, args, used, placeholders) { diag.cancel(); new_diag.emit(); return; @@ -976,7 +975,7 @@ fn expand_format_args_impl<'cx>( MacEager::expr(DummyResult::raw_expr(sp, true)) } } - Err(mut err) => { + Err(err) => { err.emit(); DummyResult::any(sp) } diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 4fddaa8ab6cd..9fb6da6e0121 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -194,7 +194,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { self.dcx .struct_span_err(attr.span, msg) - .span_label(prev_attr.span, "previous attribute here") + .span_label_mv(prev_attr.span, "previous attribute here") .emit(); return; diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 30f80bb87dd1..caebb12919fc 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -109,7 +109,7 @@ pub fn expand_include<'cx>( // The file will be added to the code map by the parser let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) { Ok(f) => f, - Err(mut err) => { + Err(err) => { err.emit(); return DummyResult::any(sp); } @@ -146,7 +146,7 @@ pub fn expand_include<'cx>( let mut ret = SmallVec::new(); loop { match self.p.parse_item(ForceCollect::No) { - Err(mut err) => { + Err(err) => { err.emit(); break; } @@ -181,7 +181,7 @@ pub fn expand_include_str( }; let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) { Ok(f) => f, - Err(mut err) => { + Err(err) => { err.emit(); return DummyResult::any(sp); } @@ -215,7 +215,7 @@ pub fn expand_include_bytes( }; let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) { Ok(f) => f, - Err(mut err) => { + Err(err) => { err.emit(); return DummyResult::any(sp); } diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 568f8247b280..717f5d9c38a2 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -409,8 +409,8 @@ fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>) ), ); } - err.span_label(attr_sp, "the `#[test]` macro causes a function to be run as a test and has no effect on non-functions") - .span_suggestion(attr_sp, + err.span_label_mv(attr_sp, "the `#[test]` macro causes a function to be run as a test and has no effect on non-functions") + .span_suggestion_mv(attr_sp, "replace with conditional compilation to make the item only exist when tests are being run", "#[cfg(test)]", Applicability::MaybeIncorrect) @@ -480,7 +480,7 @@ fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic { "argument must be of the form: \ `expected = \"error message\"`", ) - .note( + .note_mv( "errors in this attribute were erroneously \ allowed and will become a hard error in a \ future release", diff --git a/compiler/rustc_builtin_macros/src/type_ascribe.rs b/compiler/rustc_builtin_macros/src/type_ascribe.rs index 72b85af14863..564797012ae2 100644 --- a/compiler/rustc_builtin_macros/src/type_ascribe.rs +++ b/compiler/rustc_builtin_macros/src/type_ascribe.rs @@ -12,7 +12,7 @@ pub fn expand_type_ascribe( ) -> Box { let (expr, ty) = match parse_ascribe(cx, tts) { Ok(parsed) => parsed, - Err(mut err) => { + Err(err) => { err.emit(); return DummyResult::any(span); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 63fd7b42f7ba..a1ec191f1052 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -477,7 +477,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { InlineAttr::Never } else { struct_span_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument") - .help("valid inline arguments are `always` and `never`") + .help_mv("valid inline arguments are `always` and `never`") .emit(); InlineAttr::None @@ -662,7 +662,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal); tcx.dcx() .struct_span_err(attr.span, msg) - .note("the value may not exceed `u16::MAX`") + .note_mv("the value may not exceed `u16::MAX`") .emit(); None } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index b6bb1607a09c..c0ce8a54af57 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -27,7 +27,7 @@ pub fn from_target_feature( let code = "enable = \"..\""; tcx.dcx() .struct_span_err(span, msg) - .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders) + .span_suggestion_mv(span, "must be of the form", code, Applicability::HasPlaceholders) .emit(); }; let rust_features = tcx.features(); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 92955c4ed14c..738c532964a2 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -338,7 +338,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { return; } - let mut err = op.build_error(self.ccx, span); + let err = op.build_error(self.ccx, span); assert!(err.is_error()); match op.importance() { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index fd925b62702a..da3776ef96a5 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -716,7 +716,7 @@ fn print_crate_info( let result = parse_crate_attrs(sess); match result { Ok(attrs) => Some(attrs), - Err(mut parse_error) => { + Err(parse_error) => { parse_error.emit(); return Compilation::Stop; } diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index fab5affbad1e..e41acd6447bf 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -198,23 +198,34 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError { } } -/// In general, the `DiagnosticBuilder` uses deref to allow access to -/// the fields and methods of the embedded `diagnostic` in a -/// transparent way. *However,* many of the methods are intended to -/// be used in a chained way, and hence ought to return `self`. In -/// that case, we can't just naively forward to the method on the -/// `diagnostic`, because the return type would be a `&Diagnostic` -/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes -/// it easy to declare such methods on the builder. +/// `DiagnosticBuilder` impls `DerefMut`, which allows access to the fields and +/// methods of the embedded `Diagnostic`. However, that doesn't allow method +/// chaining at the `DiagnosticBuilder` level. Each use of this macro defines +/// two builder methods at that level, both of which wrap the equivalent method +/// in `Diagnostic`. +/// - A `&mut self -> &mut Self` method, with the same name as the underlying +/// `Diagnostic` method. It is mostly to modify existing diagnostics, either +/// in a standalone fashion, e.g. `err.code(code)`, or in a chained fashion +/// to make multiple modifications, e.g. `err.code(code).span(span)`. +/// - A `self -> Self` method, with `_mv` suffix added (short for "move"). +/// It is mostly used in a chained fashion when producing a new diagnostic, +/// e.g. `let err = struct_err(msg).code_mv(code)`, or when emitting a new +/// diagnostic , e.g. `struct_err(msg).code_mv(code).emit()`. +/// +/// Although the latter method can be used to modify an existing diagnostic, +/// e.g. `err = err.code_mv(code)`, this should be avoided because the former +/// method give shorter code, e.g. `err.code(code)`. macro_rules! forward { - // Forward pattern for &mut self -> &mut Self ( - $(#[$attrs:meta])* - pub fn $n:ident(&mut self $(, $name:ident: $ty:ty)* $(,)?) -> &mut Self + ($n:ident, $n_mv:ident)($($name:ident: $ty:ty),* $(,)?) ) => { - $(#[$attrs])* #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] - pub fn $n(&mut self $(, $name: $ty)*) -> &mut Self { + pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { + self.diagnostic.$n($($name),*); + self + } + #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] + pub fn $n_mv(mut self, $($name: $ty),*) -> Self { self.diagnostic.$n($($name),*); self } @@ -254,11 +265,15 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { } } - /// Emit the diagnostic. Does not consume `self`, which may be surprising, - /// but there are various places that rely on continuing to use `self` - /// after calling `emit`. + /// Emit and consume the diagnostic. #[track_caller] - pub fn emit(&mut self) -> G::EmitResult { + pub fn emit(mut self) -> G::EmitResult { + G::emit_producing_guarantee(&mut self) + } + + /// Emit the diagnostic without consuming it. `emit` should be preferred. + #[track_caller] + pub fn emit_without_consuming(&mut self) -> G::EmitResult { G::emit_producing_guarantee(self) } @@ -267,7 +282,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// /// See `emit` and `delay_as_bug` for details. #[track_caller] - pub fn emit_unless(&mut self, delay: bool) -> G::EmitResult { + pub fn emit_unless(mut self, delay: bool) -> G::EmitResult { if delay { self.downgrade_to_delayed_bug(); } @@ -354,142 +369,142 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// In the meantime, though, callsites are required to deal with the "bug" /// locally in whichever way makes the most sense. #[track_caller] - pub fn delay_as_bug(&mut self) -> G::EmitResult { + pub fn delay_as_bug(mut self) -> G::EmitResult { self.downgrade_to_delayed_bug(); self.emit() } - forward!(pub fn span_label( - &mut self, + /// Non-consuming variant of `delay_as_bug`. + #[track_caller] + pub fn delay_as_bug_without_consuming(&mut self) -> G::EmitResult { + self.downgrade_to_delayed_bug(); + self.emit_without_consuming() + } + + forward!((span_label, span_label_mv)( span: Span, - label: impl Into - ) -> &mut Self); - forward!(pub fn span_labels( - &mut self, + label: impl Into, + )); + forward!((span_labels, span_labels_mv)( spans: impl IntoIterator, label: &str, - ) -> &mut Self); - forward!(pub fn note_expected_found( - &mut self, + )); + forward!((note_expected_found, note_expected_found_mv)( expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, found_label: &dyn fmt::Display, found: DiagnosticStyledString, - ) -> &mut Self); - forward!(pub fn note_expected_found_extra( - &mut self, + )); + forward!((note_expected_found_extra, note_expected_found_extra_mv)( expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, found_label: &dyn fmt::Display, found: DiagnosticStyledString, expected_extra: &dyn fmt::Display, found_extra: &dyn fmt::Display, - ) -> &mut Self); - forward!(pub fn note(&mut self, msg: impl Into) -> &mut Self); - forward!(pub fn note_once(&mut self, msg: impl Into) -> &mut Self); - forward!(pub fn span_note( - &mut self, + )); + forward!((note, note_mv)( + msg: impl Into, + )); + forward!((note_once, note_once_mv)( + msg: impl Into, + )); + forward!((span_note, span_note_mv)( sp: impl Into, msg: impl Into, - ) -> &mut Self); - forward!(pub fn span_note_once( - &mut self, + )); + forward!((span_note_once, span_note_once_mv)( sp: impl Into, msg: impl Into, - ) -> &mut Self); - forward!(pub fn warn(&mut self, msg: impl Into) -> &mut Self); - forward!(pub fn span_warn( - &mut self, + )); + forward!((warn, warn_mv)( + msg: impl Into, + )); + forward!((span_warn, span_warn_mv)( sp: impl Into, msg: impl Into, - ) -> &mut Self); - forward!(pub fn help(&mut self, msg: impl Into) -> &mut Self); - forward!(pub fn help_once(&mut self, msg: impl Into) -> &mut Self); - forward!(pub fn span_help( - &mut self, + )); + forward!((help, help_mv)( + msg: impl Into, + )); + forward!((help_once, help_once_mv)( + msg: impl Into, + )); + forward!((span_help, span_help_once_mv)( sp: impl Into, msg: impl Into, - ) -> &mut Self); - forward!(pub fn is_lint(&mut self) -> &mut Self); - forward!(pub fn disable_suggestions(&mut self) -> &mut Self); - forward!(pub fn multipart_suggestion( - &mut self, + )); + forward!((multipart_suggestion, multipart_suggestion_mv)( msg: impl Into, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn multipart_suggestion_verbose( - &mut self, + )); + forward!((multipart_suggestion_verbose, multipart_suggestion_verbose_mv)( msg: impl Into, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn tool_only_multipart_suggestion( - &mut self, + )); + forward!((tool_only_multipart_suggestion, tool_only_multipart_suggestion_mv)( msg: impl Into, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestion( - &mut self, + )); + forward!((span_suggestion, span_suggestion_mv)( sp: Span, msg: impl Into, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestions( - &mut self, + )); + forward!((span_suggestions, span_suggestions_mv)( sp: Span, msg: impl Into, suggestions: impl IntoIterator, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn multipart_suggestions( - &mut self, + )); + forward!((multipart_suggestions, multipart_suggestions_mv)( msg: impl Into, suggestions: impl IntoIterator>, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestion_short( - &mut self, + )); + forward!((span_suggestion_short, span_suggestion_short_mv)( sp: Span, msg: impl Into, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestion_verbose( - &mut self, + )); + forward!((span_suggestion_verbose, span_suggestion_verbose_mv)( sp: Span, msg: impl Into, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestion_hidden( - &mut self, + )); + forward!((span_suggestion_hidden, span_suggestion_hidden_mv)( sp: Span, msg: impl Into, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn tool_only_span_suggestion( - &mut self, + )); + forward!((tool_only_span_suggestion, tool_only_span_suggestion_mv)( sp: Span, msg: impl Into, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn primary_message(&mut self, msg: impl Into) -> &mut Self); - forward!(pub fn span(&mut self, sp: impl Into) -> &mut Self); - forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); - forward!(pub fn arg( - &mut self, - name: impl Into>, - arg: impl IntoDiagnosticArg, - ) -> &mut Self); - forward!(pub fn subdiagnostic( - &mut self, - subdiagnostic: impl crate::AddToDiagnostic - ) -> &mut Self); + )); + forward!((primary_message, primary_message_mv)( + msg: impl Into, + )); + forward!((span, span_mv)( + sp: impl Into, + )); + forward!((code, code_mv)( + s: DiagnosticId, + )); + forward!((arg, arg_mv)( + name: impl Into>, arg: impl IntoDiagnosticArg, + )); + forward!((subdiagnostic, subdiagnostic_mv)( + subdiagnostic: impl crate::AddToDiagnostic, + )); } impl Debug for DiagnosticBuilder<'_, G> { diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 8b5a22d19146..e87f2306bc70 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1232,7 +1232,7 @@ pub fn expr_to_string( ) -> Option<(Symbol, ast::StrStyle)> { expr_to_spanned_string(cx, expr, err_msg) .map_err(|err| { - err.map(|(mut err, _)| { + err.map(|(err, _)| { err.emit(); }) }) @@ -1254,7 +1254,7 @@ pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option> { match p.parse_expr() { Ok(e) => return Some(e), - Err(mut err) => { + Err(err) => { err.emit(); } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 2283a3bfc760..d015d7799632 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -362,7 +362,7 @@ impl<'a> StripUnconfigured<'a> { pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option) { let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { Ok(meta_item) => meta_item, - Err(mut err) => { + Err(err) => { err.emit(); return (true, None); } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 5a616e0ffbff..c39a3dce34e4 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -735,7 +735,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { fragment_kind.expect_from_annotatables(items) } } - Err(mut err) => { + Err(err) => { err.emit(); fragment_kind.dummy(span) } diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index b248a1fe349b..b86831b8222d 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -679,8 +679,8 @@ impl TtParser { // We use the span of the metavariable declaration to determine any // edition-specific matching behavior for non-terminals. let nt = match parser.to_mut().parse_nonterminal(kind) { - Err(mut err) => { - let guarantee = err.span_label( + Err(err) => { + let guarantee = err.span_label_mv( span, format!( "while parsing argument for this `{kind}` macro fragment" diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index e9797abcbdf2..68296700987e 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -215,7 +215,7 @@ fn expand_macro<'cx>( // rhs has holes ( `$id` and `$(...)` that need filled) let tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) { Ok(tts) => tts, - Err(mut err) => { + Err(err) => { err.emit(); return DummyResult::any(arm_span); } diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index cd4ba7a9a62b..c3e4f40c166a 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -86,7 +86,7 @@ pub(super) fn parse( ); sess.dcx .struct_span_err(span, msg) - .help(VALID_FRAGMENT_NAMES_MSG) + .help_mv(VALID_FRAGMENT_NAMES_MSG) .emit(); token::NonterminalKind::Ident }, @@ -175,7 +175,7 @@ fn parse_tree<'a>( // of a meta-variable expression (e.g. `${count(ident)}`). // Try to parse the meta-variable expression. match MetaVarExpr::parse(tts, delim_span.entire(), sess) { - Err(mut err) => { + Err(err) => { err.emit(); // Returns early the same read `$` to avoid spanning // unrelated diagnostics that could be performed afterwards diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 60647c3350a3..e979f9a75d4d 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -282,7 +282,7 @@ impl ModError<'_> { secondary_path: secondary_path.display().to_string(), }) } - ModError::ParserError(mut err) => err.emit(), + ModError::ParserError(err) => err.emit(), } } } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 73a7d433b5c6..001b3fe44968 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -171,7 +171,7 @@ impl MultiItemModifier for DeriveProcMacro { items.push(Annotatable::Item(item)); } } - Err(mut err) => { + Err(err) => { err.emit(); break; } diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 66695e020f1d..0053f5503186 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -537,7 +537,7 @@ impl server::TokenStream for Rustc<'_, '_> { } expr }; - let expr = expr.map_err(|mut err| { + let expr = expr.map_err(|err| { err.emit(); })?; diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index d403f1a850d9..38184a5dd182 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -305,7 +305,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { binding.span, format!("{} `{}` is private", assoc_item.kind, binding.item_name), ) - .span_label(binding.span, format!("private {}", assoc_item.kind)) + .span_label_mv(binding.span, format!("private {}", assoc_item.kind)) .emit(); } tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 5e1c29440a50..24b55461a427 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -58,13 +58,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if !trait_def.paren_sugar { if trait_segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar { // For now, require that parenthetical notation be used only with `Fn()` etc. - let mut err = feature_err( + feature_err( &self.tcx().sess.parse_sess, sym::unboxed_closures, span, "parenthetical notation is only stable when used with `Fn`-family traits", - ); - err.emit(); + ) + .emit(); } return; diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index 3f0ad6584b65..257b108471f4 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -70,7 +70,7 @@ fn generic_arg_mismatch_err( Res::Err => { add_braces_suggestion(arg, &mut err); return err - .primary_message("unresolved item provided when a constant was expected") + .primary_message_mv("unresolved item provided when a constant was expected") .emit(); } Res::Def(DefKind::TyParam, src_def_id) => { diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 092df257dbfa..0a924aa3536e 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1648,8 +1648,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let def_span = tcx.def_span(item); tcx.dcx() .struct_span_err_with_code(span, msg, rustc_errors::error_code!(E0624)) - .span_label(span, format!("private {kind}")) - .span_label(def_span, format!("{kind} defined here")) + .span_label_mv(span, format!("private {kind}")) + .span_label_mv(def_span, format!("{kind} defined here")) .emit(); } tcx.check_stability(item, Some(block), span, None); diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs index 8a3df79cb253..ff84a9824952 100644 --- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs @@ -375,7 +375,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.ast_region_to_region(lifetime, None) } else { self.re_infer(None, span).unwrap_or_else(|| { - let mut err = struct_span_err!( + let err = struct_span_err!( tcx.dcx(), span, E0228, diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 5ccb7ac38964..544e06879f6f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -550,8 +550,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { E0044, "foreign items may not have {kinds} parameters", ) - .span_label(item.span, format!("can't have {kinds} parameters")) - .help( + .span_label_mv(item.span, format!("can't have {kinds} parameters")) + .help_mv( // FIXME: once we start storing spans for type arguments, turn this // into a suggestion. format!( @@ -788,7 +788,7 @@ fn check_impl_items_against_trait<'tcx>( }; tcx.dcx() .struct_span_err(tcx.def_span(def_id), msg) - .note(format!( + .note_mv(format!( "specialization behaves in inconsistent and \ surprising ways with {feature}, \ and for now is disallowed" @@ -830,7 +830,7 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { let e = fields[FieldIdx::from_u32(0)].ty(tcx, args); if !fields.iter().all(|f| f.ty(tcx, args) == e) { struct_span_err!(tcx.dcx(), sp, E0076, "SIMD vector should be homogeneous") - .span_label(sp, "SIMD elements must have the same type") + .span_label_mv(sp, "SIMD elements must have the same type") .emit(); return; } @@ -1107,7 +1107,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { E0084, "unsupported representation for zero-variant enum" ) - .span_label(tcx.def_span(def_id), "zero-variant enum") + .span_label_mv(tcx.def_span(def_id), "zero-variant enum") .emit(); } } @@ -1140,7 +1140,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { let disr_non_unit = def.variants().iter().any(|var| !is_unit(var) && has_disr(var)); if disr_non_unit || (disr_units && has_non_units) { - let mut err = struct_span_err!( + let err = struct_span_err!( tcx.dcx(), tcx.def_span(def_id), E0732, @@ -1249,7 +1249,7 @@ fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } } - if let Some(mut e) = error { + if let Some(e) = error { e.emit(); } @@ -1294,7 +1294,7 @@ pub(super) fn check_type_params_are_used<'tcx>( { let span = tcx.def_span(param.def_id); struct_span_err!(tcx.dcx(), span, E0091, "type parameter `{}` is unused", param.name,) - .span_label(span, "unused type parameter") + .span_label_mv(span, "unused type parameter") .emit(); } } @@ -1302,9 +1302,9 @@ pub(super) fn check_type_params_are_used<'tcx>( fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed { struct_span_err!(tcx.dcx(), span, E0733, "recursion in an `async fn` requires boxing") - .span_label(span, "recursive `async fn`") - .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") - .note( + .span_label_mv(span, "recursive `async fn`") + .note_mv("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") + .note_mv( "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion", ) .emit() diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index cc4dc5aca0d9..f5323fd3bb8d 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -934,12 +934,12 @@ impl<'tcx> ty::FallibleTypeFolder> for RemapHiddenTyRegions<'tcx> { return_span, "return type captures more lifetimes than trait definition", ) - .span_label(self.tcx.def_span(def_id), "this lifetime was captured") - .span_note( + .span_label_mv(self.tcx.def_span(def_id), "this lifetime was captured") + .span_note_mv( self.tcx.def_span(self.def_id), "hidden type must only reference lifetimes captured by this impl trait", ) - .note(format!("hidden type inferred to be `{}`", self.ty)) + .note_mv(format!("hidden type inferred to be `{}`", self.ty)) .emit() } _ => { diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 3492499db686..ff78d040aca1 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -161,7 +161,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( "`Drop` impl requires `{root_predicate}` \ but the {self_descr} it is implemented for does not", ) - .span_note(item_span, "the implementor must specify the same requirement") + .span_note_mv(item_span, "the implementor must specify the same requirement") .emit(), ); } @@ -193,7 +193,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( "`Drop` impl requires `{outlives}` \ but the {self_descr} it is implemented for does not", ) - .span_note(item_span, "the implementor must specify the same requirement") + .span_note_mv(item_span, "the implementor must specify the same requirement") .emit(), ); } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 632af780ed85..a5aedeb33ae9 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -30,7 +30,7 @@ fn equate_intrinsic_type<'tcx>( } _ => { struct_span_err!(tcx.dcx(), it.span, E0622, "intrinsic must be a function") - .span_label(it.span, "expected a function") + .span_label_mv(it.span, "expected a function") .emit(); return; } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 5f26da9c87f4..c53ff5655500 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -838,8 +838,8 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem trait_should_be_self, "associated item referring to unboxed trait object for its own trait", ) - .span_label(trait_name.span, "in this trait") - .multipart_suggestion( + .span_label_mv(trait_name.span, "in this trait") + .multipart_suggestion_mv( "you might have meant to use `Self` to refer to the implementing type", sugg, Applicability::MachineApplicable, @@ -1599,7 +1599,7 @@ fn check_method_receiver<'tcx>( the `arbitrary_self_types` feature", ), ) - .help(HELP_FOR_SELF_TYPE) + .help_mv(HELP_FOR_SELF_TYPE) .emit() } else { // Report error; would not have worked with `arbitrary_self_types`. @@ -1612,8 +1612,8 @@ fn check_method_receiver<'tcx>( fn e0307(tcx: TyCtxt<'_>, span: Span, receiver_ty: Ty<'_>) -> ErrorGuaranteed { struct_span_err!(tcx.dcx(), span, E0307, "invalid `self` parameter type: {receiver_ty}") - .note("type of `self` must be `Self` or a type that dereferences to it") - .help(HELP_FOR_SELF_TYPE) + .note_mv("type of `self` must be `Self` or a type that dereferences to it") + .help_mv(HELP_FOR_SELF_TYPE) .emit() } diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 5cc9da25d6a5..3d56f8fd44ea 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -181,7 +181,7 @@ fn check_object_overlap<'tcx>( trait_ref.self_ty(), tcx.def_path_str(trait_def_id) ) - .span_label( + .span_label_mv( span, format!( "`{}` automatically implements trait `{}`", diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index d208c55055b8..272c13b4c1c8 100644 --- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -25,7 +25,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "implementing the trait `{}` is not unsafe", trait_ref.print_trait_sugared() ) - .span_suggestion_verbose( + .span_suggestion_verbose_mv( item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), "remove `unsafe` from this trait implementation", "", @@ -42,13 +42,13 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "the trait `{}` requires an `unsafe impl` declaration", trait_ref.print_trait_sugared() ) - .note(format!( + .note_mv(format!( "the trait `{}` enforces invariants that the compiler can't check. \ Review the trait documentation and make sure this implementation \ upholds those invariants before adding the `unsafe` keyword", trait_ref.print_trait_sugared() )) - .span_suggestion_verbose( + .span_suggestion_verbose_mv( item.span.shrink_to_lo(), "add `unsafe` to this trait implementation", "unsafe ", @@ -65,13 +65,13 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "requires an `unsafe impl` declaration due to `#[{}]` attribute", attr_name ) - .note(format!( + .note_mv(format!( "the trait `{}` enforces invariants that the compiler can't check. \ Review the trait documentation and make sure this implementation \ upholds those invariants before adding the `unsafe` keyword", trait_ref.print_trait_sugared() )) - .span_suggestion_verbose( + .span_suggestion_verbose_mv( item.span.shrink_to_lo(), "add `unsafe` to this trait implementation", "unsafe ", 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 9a28534d7900..c033cec31550 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -2114,7 +2114,7 @@ pub fn deny_non_region_late_bound( hir::GenericParamKind::Lifetime { .. } => continue, }; - let mut diag = tcx.dcx().struct_span_err( + let diag = tcx.dcx().struct_span_err( param.span, format!("late-bound {what} parameter not allowed on {where_}"), ); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index de2cb5a6d5c7..8939c2ddea54 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -402,7 +402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_expr.span, format!("evaluate({predicate:?}) = {result:?}"), ) - .span_label(predicate_span, "predicate") + .span_label_mv(predicate_span, "predicate") .emit(); } } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 631854444793..0a9f2a27cb83 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -269,7 +269,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } CastError::NeedViaInt => { make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx) - .help("cast through an integer first") + .help_mv("cast through an integer first") .emit(); } CastError::IllegalCast => { @@ -277,7 +277,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } CastError::DifferingKinds => { make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx) - .note("vtable kinds may not match") + .note_mv("vtable kinds may not match") .emit(); } CastError::CastToBool => { @@ -512,7 +512,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { self.cast_ty, fcx, ) - .note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate") + .note_mv("cannot cast an enum with a non-exhaustive variant when it's defined in another crate") .emit(); } } diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index c717319e5072..9850892bd36a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -158,7 +158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Requires that the two types unify, and prints an error message if /// they don't. pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { - if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) { + if let Some(e) = self.demand_suptype_diag(sp, expected, actual) { e.emit(); } } @@ -189,7 +189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { - if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) { + if let Some(err) = self.demand_eqtype_diag(sp, expected, actual) { err.emit(); } } @@ -228,7 +228,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase); - if let Some(mut err) = err { + if let Some(err) = err { err.emit(); } ty diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 44d9f1ed8184..69a65e8b750b 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1338,7 +1338,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Err(error) => { if segment.ident.name != kw::Empty { - if let Some(mut err) = self.report_method_error( + if let Some(err) = self.report_method_error( span, rcvr_t, segment.ident, @@ -2011,7 +2011,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { != range_def_id { // Suppress any range expr type mismatches - if let Some(mut diag) = + if let Some(diag) = self.dcx().steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo) { diag.delay_as_bug(); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index cb109a2e0242..a4cd9ccc984e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -845,7 +845,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .and_then(|r| { // lint bare trait if the method is found in the trait if span.edition().at_least_rust_2021() - && let Some(mut diag) = + && let Some(diag) = self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { diag.emit(); @@ -877,7 +877,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // emit or cancel the diagnostic for bare traits if span.edition().at_least_rust_2021() - && let Some(mut diag) = + && let Some(diag) = self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { if trait_missing_method { @@ -889,7 +889,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if item_name.name != kw::Empty { - if let Some(mut e) = self.report_method_error( + if let Some(e) = self.report_method_error( span, ty.normalized, item_name, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 7b85c492eae5..9b8916911618 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1384,7 +1384,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "expected struct, variant or union type, found {}", ty.normalized.sort_string(self.tcx) ) - .span_label(path_span, "not a struct") + .span_label_mv(path_span, "not a struct") .emit(), }) } @@ -1459,8 +1459,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let previous_diverges = self.diverges.get(); let else_ty = self.check_block_with_expected(blk, NoExpectation); let cause = self.cause(blk.span, ObligationCauseCode::LetElse); - if let Some(mut err) = - self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) + if let Some(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) { err.emit(); } diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 44d28123fa3f..a18f486b0d25 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -74,9 +74,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && size_to == Pointer(dl.instruction_address_space).size(&tcx) { struct_span_err!(tcx.dcx(), span, E0591, "can't transmute zero-sized type") - .note(format!("source type: {from}")) - .note(format!("target type: {to}")) - .help("cast with `as` to a pointer instead") + .note_mv(format!("source type: {from}")) + .note_mv(format!("target type: {to}")) + .help_mv("cast with `as` to a pointer instead") .emit(); return; } @@ -125,9 +125,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) .note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); if let Err(LayoutError::ReferencesError(_)) = sk_from { - err.delay_as_bug(); + err.delay_as_bug_without_consuming(); } else if let Err(LayoutError::ReferencesError(_)) = sk_to { - err.delay_as_bug(); + err.delay_as_bug_without_consuming(); } } err.emit(); diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 6044b1fdd40a..c3332a493067 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -366,7 +366,7 @@ fn report_unexpected_variant_res( _ => res.descr(), }; let path_str = rustc_hir_pretty::qpath_to_string(qpath); - let mut err = tcx.dcx().struct_span_err_with_code( + let err = tcx.dcx().struct_span_err_with_code( span, format!("expected {expected}, found {res_descr} `{path_str}`"), DiagnosticId::Error(err_code.into()), @@ -374,10 +374,10 @@ fn report_unexpected_variant_res( match res { Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == "E0164" => { let patterns_url = "https://doc.rust-lang.org/book/ch18-00-patterns.html"; - err.span_label(span, "`fn` calls are not allowed in patterns"); - err.help(format!("for more information, visit {patterns_url}")) + err.span_label_mv(span, "`fn` calls are not allowed in patterns") + .help_mv(format!("for more information, visit {patterns_url}")) } - _ => err.span_label(span, format!("not a {expected}")), + _ => err.span_label_mv(span, format!("not a {expected}")), } .emit() } diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 7b49a7cc009d..59cf0d8c8ed6 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -385,7 +385,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::ExprKind::Assign(..) = expr.kind { // We defer to the later error produced by `check_lhs_assignable`. - err.delay_as_bug(); + err.delay_as_bug_without_consuming(); } let suggest_deref_binop = diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 02a35110716b..022213abd670 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -117,7 +117,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { actual: Ty<'tcx>, ti: TopInfo<'tcx>, ) { - if let Some(mut err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) { + if let Some(err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) { err.emit(); } } @@ -441,7 +441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // then that's equivalent to there existing a LUB. let cause = self.pattern_cause(ti, span); - if let Some(mut err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) { + if let Some(err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) { err.emit_unless( ti.span .filter(|&s| { @@ -1103,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check the tuple struct pattern against the expected type. let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti); - let had_err = if let Some(mut err) = diag { + let had_err = if let Some(err) = diag { err.emit(); true } else { @@ -1329,9 +1329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let element_tys = tcx.mk_type_list_from_iter(element_tys_iter); let pat_ty = Ty::new_tup(tcx, element_tys); - if let Some(mut err) = - self.demand_eqtype_pat_diag(span, expected, pat_ty, pat_info.top_info) - { + if let Some(err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, pat_info.top_info) { let reported = err.emit(); // Walk subpatterns with an expected type of `err` in this case to silence // further errors being emitted when using the bindings. #50333 @@ -1469,8 +1467,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } match (inexistent_fields_err, unmentioned_err) { - (Some(mut i), Some(mut u)) => { - if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { + (Some(i), Some(u)) => { + if let Some(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { // We don't want to show the nonexistent fields error when this was // `Foo { a, b }` when it should have been `Foo(a, b)`. i.delay_as_bug(); @@ -1481,19 +1479,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { u.emit(); } } - (None, Some(mut u)) => { - if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { + (None, Some(u)) => { + if let Some(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { u.delay_as_bug(); e.emit(); } else { u.emit(); } } - (Some(mut err), None) => { + (Some(err), None) => { err.emit(); } (None, None) - if let Some(mut err) = + if let Some(err) = self.error_tuple_variant_index_shorthand(variant, pat, fields) => { err.emit(); @@ -1571,8 +1569,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "field `{}` bound multiple times in the pattern", ident ) - .span_label(span, format!("multiple uses of `{ident}` in pattern")) - .span_label(other_field, format!("first use of `{ident}`")) + .span_label_mv(span, format!("multiple uses of `{ident}` in pattern")) + .span_label_mv(other_field, format!("first use of `{ident}`")) .emit() } @@ -2237,7 +2235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pluralize!(min_len), size, ) - .span_label(span, format!("expected {} element{}", size, pluralize!(size))) + .span_label_mv(span, format!("expected {} element{}", size, pluralize!(size))) .emit() } @@ -2256,7 +2254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pluralize!(min_len), size, ) - .span_label( + .span_label_mv( span, format!("pattern cannot match array of {} element{}", size, pluralize!(size),), ) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index b5a6374ec4b4..f88fd617c13e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -361,7 +361,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( ); } ty::ReError(_) => { - err.delay_as_bug(); + err.delay_as_bug_without_consuming(); } _ => { // Ugh. This is a painful case: the hidden region is not one diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index e13d1d38a89f..d3e28446ddef 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -60,7 +60,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { pub fn try_report(&self) -> Option { self.try_report_from_nll() - .map(|mut diag| diag.emit()) + .map(|diag| diag.emit()) .or_else(|| self.try_report_impl_not_conforming_to_trait()) .or_else(|| self.try_report_anon_anon_conflict()) .or_else(|| self.try_report_static_impl_trait()) diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index ae30cf53c71e..e9fa39ad08a2 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -281,7 +281,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } }; if sub.is_error() || sup.is_error() { - err.delay_as_bug(); + err.delay_as_bug_without_consuming(); } err } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 07bbe78dc2d4..9da806e0779a 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -108,7 +108,7 @@ impl<'tcx> Queries<'tcx> { pub fn parse(&self) -> Result> { self.parse.compute(|| { - passes::parse(&self.compiler.sess).map_err(|mut parse_error| parse_error.emit()) + passes::parse(&self.compiler.sess).map_err(|parse_error| parse_error.emit()) }) } diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index fc4d4c9a3d26..d9bcd8d3ae72 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -132,7 +132,7 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { .tcx .dcx() .struct_span_err(self.span, "non-defining opaque type use in defining scope") - .span_label( + .span_label_mv( self.span, format!( "lifetime `{r}` is part of concrete type but not used in \ diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 3a6fbaec9fd4..b9c75bd205b2 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -369,7 +369,7 @@ impl<'tcx> TyCtxt<'tcx> { if let Some((old_item_id, _)) = dtor_candidate { self.dcx() .struct_span_err(self.def_span(item_id), "multiple drop impls found") - .span_note(self.def_span(old_item_id), "other impl here") + .span_note_mv(self.def_span(old_item_id), "other impl here") .delay_as_bug(); } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index b6cccd275ee6..72f76cd89756 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -278,7 +278,7 @@ impl<'a> TokenTreesReader<'a> { } if !diff_errs.is_empty() { errs.iter_mut().for_each(|err| { - err.delay_as_bug(); + err.delay_as_bug_without_consuming(); }); return diff_errs; } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 579c3cffcfbf..d7604d7cebca 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -245,9 +245,9 @@ pub fn parse_cfg_attr( crate::validate_attr::check_cfg_attr_bad_delim(parse_sess, dspan, delim); match parse_in(parse_sess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { Ok(r) => return Some(r), - Err(mut e) => { - e.help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`")) - .note(CFG_ATTR_NOTE_REF) + Err(e) => { + e.help_mv(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`")) + .note_mv(CFG_ATTR_NOTE_REF) .emit(); } } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index aed5e11133bc..02eab2bac58b 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -896,7 +896,7 @@ impl<'a> Parser<'a> { let struct_expr = snapshot.parse_expr_struct(None, path, false); let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No); return Some(match (struct_expr, block_tail) { - (Ok(expr), Err(mut err)) => { + (Ok(expr), Err(err)) => { // We have encountered the following: // fn foo() -> Foo { // field: value, @@ -1218,7 +1218,7 @@ impl<'a> Parser<'a> { "::", Applicability::MaybeIncorrect, ) - .emit(); + .emit_without_consuming(); match self.parse_expr() { Ok(_) => { *expr = self.mk_expr_err(expr.span.to(self.prev_token.span)); @@ -2045,7 +2045,7 @@ impl<'a> Parser<'a> { ) -> P { match result { Ok(x) => x, - Err(mut err) => { + Err(err) => { err.emit(); // Recover from parse error, callers expect the closing delim to be consumed. self.consume_block(delim, ConsumeClosingDelim::Yes); @@ -2470,7 +2470,7 @@ impl<'a> Parser<'a> { return Ok(true); // Continue } } - Err(mut err) => { + Err(err) => { args.push(arg); // We will emit a more generic error later. err.delay_as_bug(); @@ -2946,7 +2946,7 @@ impl<'a> Parser<'a> { } pub fn recover_diff_marker(&mut self) { - if let Err(mut err) = self.err_diff_marker() { + if let Err(err) = self.err_diff_marker() { err.emit(); FatalError.raise(); } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 880743ddd3cf..697c44a9f3e2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -127,7 +127,7 @@ impl<'a> Parser<'a> { fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P> { match self.parse_expr_res(restrictions, None) { Ok(expr) => Ok(expr), - Err(mut err) => match self.token.ident() { + Err(err) => match self.token.ident() { Some((Ident { name: kw::Underscore, .. }, false)) if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) => { @@ -1333,12 +1333,12 @@ impl<'a> Parser<'a> { .collect(), }, }); - replacement_err.emit(); + replacement_err.emit_without_consuming(); let old_err = mem::replace(err, replacement_err); old_err.cancel(); } else { - err.emit(); + err.emit_without_consuming(); } return Some(self.mk_expr_err(span)); } @@ -1756,9 +1756,8 @@ impl<'a> Parser<'a> { mk_lit_char: impl FnOnce(Symbol, Span) -> L, err: impl FnOnce(&Self) -> DiagnosticBuilder<'a>, ) -> L { - if let Some(mut diag) = self.dcx().steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar) - { - diag.span_suggestion_verbose( + if let Some(diag) = self.dcx().steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar) { + diag.span_suggestion_verbose_mv( lifetime.span.shrink_to_hi(), "add `'` to close the char literal", "'", @@ -1767,7 +1766,7 @@ impl<'a> Parser<'a> { .emit(); } else { err(self) - .span_suggestion_verbose( + .span_suggestion_verbose_mv( lifetime.span.shrink_to_hi(), "add `'` to close the char literal", "'", @@ -2903,7 +2902,7 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(Delimiter::Brace) { match self.parse_arm() { Ok(arm) => arms.push(arm), - Err(mut e) => { + Err(e) => { // Recover by skipping to the end of the block. e.emit(); self.recover_stmt(); @@ -3437,7 +3436,7 @@ impl<'a> Parser<'a> { } match self.parse_expr() { Ok(e) => base = ast::StructRest::Base(e), - Err(mut e) if recover => { + Err(e) if recover => { e.emit(); self.recover_stmt(); } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 7e243c1c32a9..2a8e220181a4 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -77,7 +77,7 @@ impl<'a> Parser<'a> { Applicability::MachineApplicable, ); } - err.emit(); + err.emit_without_consuming(); return Err(err); } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 2ce27ff66e1d..45aafbd0fdf3 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -739,11 +739,14 @@ impl<'a> Parser<'a> { break; } Ok(Some(item)) => items.extend(item), - Err(mut err) => { + Err(err) => { self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); - err.span_label(open_brace_span, "while parsing this item list starting here") - .span_label(self.prev_token.span, "the item list ends here") - .emit(); + err.span_label_mv( + open_brace_span, + "while parsing this item list starting here", + ) + .span_label_mv(self.prev_token.span, "the item list ends here") + .emit(); break; } } @@ -762,8 +765,8 @@ impl<'a> Parser<'a> { E0584, "found a documentation comment that doesn't document anything", ) - .span_label(self.token.span, "this doc comment doesn't document anything") - .help( + .span_label_mv(self.token.span, "this doc comment doesn't document anything") + .help_mv( "doc comments must come before what they document, if a comment was \ intended use `//`", ) @@ -1106,7 +1109,7 @@ impl<'a> Parser<'a> { && self.token.is_keyword(kw::Unsafe) && self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Brace)) { - let mut err = self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err(); + let err = self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err(); err.emit(); unsafety = Unsafe::Yes(self.token.span); self.eat_keyword(kw::Unsafe); @@ -1198,7 +1201,7 @@ impl<'a> Parser<'a> { defaultness: Defaultness, ) -> PResult<'a, ItemInfo> { let impl_span = self.token.span; - let mut err = self.expected_ident_found_err(); + let err = self.expected_ident_found_err(); // Only try to recover if this is implementing a trait for a type let mut impl_info = match self.parse_item_impl(attrs, defaultness) { @@ -1216,7 +1219,7 @@ impl<'a> Parser<'a> { let before_trait = trai.path.span.shrink_to_lo(); let const_up_to_impl = const_span.with_hi(impl_span.lo()); - err.multipart_suggestion( + err.multipart_suggestion_mv( "you might have meant to write a const trait impl", vec![(const_up_to_impl, "".to_owned()), (before_trait, "const ".to_owned())], Applicability::MaybeIncorrect, @@ -1454,8 +1457,8 @@ impl<'a> Parser<'a> { let ident = this.parse_field_ident("enum", vlo)?; if this.token == token::Not { - if let Err(mut err) = this.unexpected::<()>() { - err.note(fluent::parse_macro_expands_to_enum_variant).emit(); + if let Err(err) = this.unexpected::<()>() { + err.note_mv(fluent::parse_macro_expands_to_enum_variant).emit(); } this.bump(); @@ -1811,7 +1814,7 @@ impl<'a> Parser<'a> { // `check_trailing_angle_brackets` already emitted a nicer error // NOTE(eddyb) this was `.cancel()`, but `err` // gets returned, so we can't fully defuse it. - err.delay_as_bug(); + err.delay_as_bug_without_consuming(); } } } @@ -1828,7 +1831,7 @@ impl<'a> Parser<'a> { ",", Applicability::MachineApplicable, ); - err.emit(); + err.emit_without_consuming(); recovered = true; } @@ -1846,7 +1849,7 @@ impl<'a> Parser<'a> { } fn expect_field_ty_separator(&mut self) -> PResult<'a, ()> { - if let Err(mut err) = self.expect(&token::Colon) { + if let Err(err) = self.expect(&token::Colon) { let sm = self.sess.source_map(); let eq_typo = self.token.kind == token::Eq && self.look_ahead(1, |t| t.is_path_start()); let semi_typo = self.token.kind == token::Semi @@ -1862,7 +1865,7 @@ impl<'a> Parser<'a> { if eq_typo || semi_typo { self.bump(); // Gracefully handle small typos. - err.span_suggestion_short( + err.span_suggestion_short_mv( self.prev_token.span, "field names and their types are separated with `:`", ":", @@ -2598,7 +2601,7 @@ impl<'a> Parser<'a> { let (mut params, _) = self.parse_paren_comma_seq(|p| { p.recover_diff_marker(); let snapshot = p.create_snapshot_for_diagnostic(); - let param = p.parse_param_general(req_name, first_param).or_else(|mut e| { + let param = p.parse_param_general(req_name, first_param).or_else(|e| { e.emit(); let lo = p.prev_token.span; p.restore_snapshot(snapshot); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index b201d36455e6..c13adfb0532d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -499,7 +499,7 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err(recover)?; if !is_raw && ident.is_reserved() { - let mut err = self.expected_ident_found_err(); + let err = self.expected_ident_found_err(); if recover { err.emit(); } else { @@ -847,7 +847,7 @@ impl<'a> Parser<'a> { pprust::token_to_string(&self.prev_token) ); expect_err - .span_suggestion_verbose( + .span_suggestion_verbose_mv( self.prev_token.span.shrink_to_hi().until(self.token.span), msg, " @ ", @@ -863,7 +863,7 @@ impl<'a> Parser<'a> { // Parsed successfully, therefore most probably the code only // misses a separator. expect_err - .span_suggestion_short( + .span_suggestion_short_mv( sp, format!("missing `{token_str}`"), token_str, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7d17b1d4c4d6..e9b68a129ef6 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -144,7 +144,7 @@ impl<'a> Parser<'a> { // Parse the first pattern (`p_0`). let mut first_pat = match self.parse_pat_no_top_alt(expected, syntax_loc) { Ok(pat) => pat, - Err(mut err) + Err(err) if self.token.is_reserved_ident() && !self.token.is_keyword(kw::In) && !self.token.is_keyword(kw::If) => @@ -251,7 +251,7 @@ impl<'a> Parser<'a> { } }); if trailing_vert { - err.delay_as_bug(); + err.delay_as_bug_without_consuming(); } err.emit(); } @@ -1028,7 +1028,7 @@ impl<'a> Parser<'a> { let attrs = match self.parse_outer_attributes() { Ok(attrs) => attrs, Err(err) => { - if let Some(mut delayed) = delayed_err { + if let Some(delayed) = delayed_err { delayed.emit(); } return Err(err); @@ -1040,7 +1040,7 @@ impl<'a> Parser<'a> { if !ate_comma { let mut err = self.dcx().create_err(ExpectedCommaAfterPatternField { span: self.token.span }); - if let Some(mut delayed) = delayed_err { + if let Some(delayed) = delayed_err { delayed.emit(); } self.recover_misplaced_pattern_modifiers(&fields, &mut err); @@ -1113,14 +1113,14 @@ impl<'a> Parser<'a> { // This way we avoid "pattern missing fields" errors afterwards. // We delay this error until the end in order to have a span for a // suggested fix. - if let Some(mut delayed_err) = delayed_err { + if let Some(delayed_err) = delayed_err { delayed_err.emit(); return Err(err); } else { delayed_err = Some(err); } } else { - if let Some(mut err) = delayed_err { + if let Some(err) = delayed_err { err.emit(); } return Err(err); @@ -1132,7 +1132,7 @@ impl<'a> Parser<'a> { let field = match this.parse_pat_field(lo, attrs) { Ok(field) => Ok(field), Err(err) => { - if let Some(mut delayed_err) = delayed_err.take() { + if let Some(delayed_err) = delayed_err.take() { delayed_err.emit(); } return Err(err); diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 4253c0ae421a..5ad17a30980f 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -128,7 +128,7 @@ impl<'a> Parser<'a> { self.prev_token.span, "found single colon before projection in qualified path", ) - .span_suggestion( + .span_suggestion_mv( self.prev_token.span, "use double colon", "::", @@ -493,7 +493,7 @@ impl<'a> Parser<'a> { self.angle_bracket_nesting -= 1; Ok(args) } - Err(mut e) if self.angle_bracket_nesting > 10 => { + Err(e) if self.angle_bracket_nesting > 10 => { self.angle_bracket_nesting -= 1; // When encountering severely malformed code where there are several levels of // nested unclosed angle args (`f:: Parser<'a> { /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. // Public for rustfmt usage. pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option> { - Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|mut e| { + Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| { e.emit(); self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); None @@ -663,7 +663,7 @@ impl<'a> Parser<'a> { match expect_result { // Recover from parser, skip type error to avoid extra errors. Ok(true) => true, - Err(mut e) => { + Err(e) => { if self.recover_colon_as_semi() { // recover_colon_as_semi has already emitted a nicer error. e.delay_as_bug(); @@ -716,7 +716,7 @@ impl<'a> Parser<'a> { _ => {} } - if let Err(mut e) = + if let Err(e) = self.check_mistyped_turbofish_with_multiple_type_params(e, expr) { if recover.no() { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 4be2c662d035..61d72857c36b 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -300,7 +300,7 @@ impl<'a> Parser<'a> { let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); let kind = self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)?; - let mut err = self.dcx().create_err(errors::TransposeDynOrImpl { + let err = self.dcx().create_err(errors::TransposeDynOrImpl { span: kw.span, kw: kw.name.as_str(), sugg: errors::TransposeDynOrImplSugg { @@ -487,7 +487,7 @@ impl<'a> Parser<'a> { fn parse_array_or_slice_ty(&mut self) -> PResult<'a, TyKind> { let elt_ty = match self.parse_ty() { Ok(ty) => ty, - Err(mut err) + Err(err) if self.look_ahead(1, |t| t.kind == token::CloseDelim(Delimiter::Bracket)) | self.look_ahead(1, |t| t.kind == token::Semi) => { @@ -1109,20 +1109,19 @@ impl<'a> Parser<'a> { lifetime_defs.append(&mut generic_params); let generic_args_span = generic_args.span(); - let mut err = self - .dcx() - .struct_span_err(generic_args_span, "`Fn` traits cannot take lifetime parameters"); let snippet = format!( "for<{}> ", lifetimes.iter().map(|lt| lt.ident.as_str()).intersperse(", ").collect::(), ); let before_fn_path = fn_path.span.shrink_to_lo(); - err.multipart_suggestion( - "consider using a higher-ranked trait bound instead", - vec![(generic_args_span, "".to_owned()), (before_fn_path, snippet)], - Applicability::MaybeIncorrect, - ) - .emit(); + self.dcx() + .struct_span_err(generic_args_span, "`Fn` traits cannot take lifetime parameters") + .multipart_suggestion_mv( + "consider using a higher-ranked trait bound instead", + vec![(generic_args_span, "".to_owned()), (before_fn_path, snippet)], + Applicability::MaybeIncorrect, + ) + .emit(); Ok(()) } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 4efb1be03bee..724d574349a8 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -29,7 +29,7 @@ pub fn check_attr(sess: &ParseSess, attr: &Attribute) { _ if let AttrArgs::Eq(..) = attr.get_normal_item().args => { // All key-value attributes are restricted to meta-item syntax. parse_meta(sess, attr) - .map_err(|mut err| { + .map_err(|err| { err.emit(); }) .ok(); @@ -139,7 +139,7 @@ pub fn check_builtin_attribute( ) { match parse_meta(sess, attr) { Ok(meta) => check_builtin_meta_item(sess, &meta, attr.style, name, template), - Err(mut err) => { + Err(err) => { err.emit(); } } @@ -208,7 +208,7 @@ fn emit_malformed_attribute( } else { sess.dcx .struct_span_err(span, error_msg) - .span_suggestions( + .span_suggestions_mv( span, if suggestions.len() == 1 { "must be of the form" diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index f4cd2fbc91fc..42e929bde2ca 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -48,7 +48,7 @@ impl DebuggerVisualizerCollector<'_> { let file = match resolve_path(&self.sess.parse_sess, visualizer_path.as_str(), attr.span) { Ok(file) => file, - Err(mut err) => { + Err(err) => { err.emit(); return; } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 9bd9dd41cf69..44c71dfa8d02 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1040,7 +1040,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'_, G> for BreakNonLoop<'a> { // This error is redundant, we will have already emitted a // suggestion to use the label when `segment` wasn't found // (hence the `Res::Err` check). - diag.delay_as_bug(); + diag.delay_as_bug_without_consuming(); } _ => { diag.span_suggestion( diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 51842664eeb7..c5715d938595 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -124,7 +124,7 @@ fn handle_cycle_error( query: Q, qcx: Qcx, cycle_error: &CycleError, - mut error: DiagnosticBuilder<'_>, + error: DiagnosticBuilder<'_>, ) -> Q::Value where Q: QueryConfig, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 4e1fda5479c8..e176b8b4043c 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -818,7 +818,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.r .dcx() .struct_span_err(item.span, "`extern crate self;` requires renaming") - .span_suggestion( + .span_suggestion_mv( item.span, "rename the `self` crate to be able to import it", "extern crate self as name;", @@ -999,7 +999,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { let msg = format!("`{name}` is already in scope"); let note = "macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)"; - self.r.dcx().struct_span_err(span, msg).note(note).emit(); + self.r.dcx().struct_span_err(span, msg).note_mv(note).emit(); } } @@ -1110,10 +1110,9 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { let msg = "`#[macro_escape]` is a deprecated synonym for `#[macro_use]`"; let mut err = self.r.dcx().struct_span_warn(attr.span, msg); if let ast::AttrStyle::Inner = attr.style { - err.help("try an outer attribute: `#[macro_use]`").emit(); - } else { - err.emit(); + err.help("try an outer attribute: `#[macro_use]`"); } + err.emit(); } else if !attr.has_name(sym::macro_use) { continue; } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 972976622002..c5bd7ffa038c 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1233,7 +1233,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ); } else { if ns == TypeNS { - let mut err = if crate_private_reexport { + let err = if crate_private_reexport { self.dcx().create_err(CannotBeReexportedCratePublicNS { span: import.span, ident, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index a82f7bdfbf3f..059ccaebc820 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2600,7 +2600,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { E0637, "`'_` cannot be used here" ) - .span_label(param.ident.span, "`'_` is a reserved lifetime name") + .span_label_mv(param.ident.span, "`'_` is a reserved lifetime name") .emit(); // Record lifetime res, so lowering knows there is something fishy. self.record_lifetime_param(param.id, LifetimeRes::Error); @@ -2615,7 +2615,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { "invalid lifetime parameter name: `{}`", param.ident, ) - .span_label(param.ident.span, "'static is a reserved lifetime name") + .span_label_mv(param.ident.span, "'static is a reserved lifetime name") .emit(); // Record lifetime res, so lowering knows there is something fishy. self.record_lifetime_param(param.id, LifetimeRes::Error); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 0f7294cdad09..32aed6611cfc 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -128,7 +128,7 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { let msg = format!("{} `{}` was already registered", "tool", ident); tcx.dcx() .struct_span_err(ident.span, msg) - .span_label(old_ident.span, "already registered here") + .span_label_mv(old_ident.span, "already registered here") .emit(); } } @@ -137,7 +137,7 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { let span = nested_meta.span(); tcx.dcx() .struct_span_err(span, msg) - .span_label(span, "not an identifier") + .span_label_mv(span, "not an identifier") .emit(); } } @@ -955,7 +955,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { E0773, "attempted to define built-in macro more than once" ) - .span_note(span, "previously defined here") + .span_note_mv(span, "previously defined here") .emit(); } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ce166ae352fd..8491d9b4abcb 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1491,7 +1491,10 @@ impl EarlyDiagCtxt { jobserver::initialize_checked(|err| { #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - self.dcx.struct_warn(err).note("the build environment is likely misconfigured").emit() + self.dcx + .struct_warn(err) + .note_mv("the build environment is likely misconfigured") + .emit() }); } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 451e0823c252..e119ddeb4c6e 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -122,8 +122,8 @@ pub fn is_const_evaluatable<'tcx>( if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def) } else { span }, "failed to evaluate generic const expression", ) - .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") - .span_suggestion_verbose( + .note_mv("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") + .span_suggestion_verbose_mv( rustc_span::DUMMY_SP, "consider enabling this feature", "#![feature(generic_const_exprs)]\n", diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 58700850a60f..fb1df4e4e766 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2088,7 +2088,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let ty = self.resolve_vars_if_possible(returned_ty); if ty.references_error() { // don't print out the [type error] here - err.delay_as_bug(); + err.delay_as_bug_without_consuming(); } else { err.span_label(expr.span, format!("this returned value is of type `{ty}`")); } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 54b91ab1d4d8..c1145bc30c91 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -969,7 +969,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.tcx().def_span(impl_def_id), "multiple drop impls found", ) - .span_note(self.tcx().def_span(old_impl_def_id), "other impl here") + .span_note_mv( + self.tcx().def_span(old_impl_def_id), + "other impl here", + ) .delay_as_bug(); } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index d43ab0c8e85a..10329c623b38 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -345,7 +345,7 @@ fn report_negative_positive_conflict<'tcx>( positive_impl_def_id: DefId, sg: &mut specialization_graph::Graph, ) { - let mut err = tcx.dcx().create_err(NegativePositiveConflict { + let err = tcx.dcx().create_err(NegativePositiveConflict { impl_span: tcx.def_span(local_impl_def_id), trait_desc: overlap.trait_ref, self_ty: overlap.self_ty, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1a25d3f79936..0e81b31dd6c4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -3001,7 +3001,7 @@ fn clean_use_statement_inner<'tcx>( E0780, "anonymous imports cannot be inlined" ) - .span_label(import.span, "anonymous import") + .span_label_mv(import.span, "anonymous import") .emit(); } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index f3d63fad7115..614a64d40201 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -577,13 +577,13 @@ impl Options { { if !theme_file.is_file() { dcx.struct_err(format!("invalid argument: \"{theme_s}\"")) - .help("arguments to --theme must be files") + .help_mv("arguments to --theme must be files") .emit(); return Err(1); } if theme_file.extension() != Some(OsStr::new("css")) { dcx.struct_err(format!("invalid argument: \"{theme_s}\"")) - .help("arguments to --theme must have a .css extension") + .help_mv("arguments to --theme must have a .css extension") .emit(); return Err(1); } @@ -595,8 +595,8 @@ impl Options { dcx.struct_warn(format!( "theme file \"{theme_s}\" is missing CSS rules from the default theme", )) - .warn("the theme may appear incorrect when loaded") - .help(format!( + .warn_mv("the theme may appear incorrect when loaded") + .help_mv(format!( "to see what rules are missing, call `rustdoc --check-theme \"{theme_s}\"`", )) .emit(); @@ -809,7 +809,7 @@ fn check_deprecated_options(matches: &getopts::Matches, dcx: &rustc_errors::Diag for &flag in deprecated_flags.iter() { if matches.opt_present(flag) { dcx.struct_warn(format!("the `{flag}` flag is deprecated")) - .note( + .note_mv( "see issue #44136 \ for more information", ) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 506eb56fdc8f..176494a38634 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1228,7 +1228,7 @@ impl LinkCollector<'_, '_> { span, "linking to associated items of raw pointers is experimental", ) - .note("rustdoc does not allow disambiguating between `*const` and `*mut`, and pointers are unstable until it does") + .note_mv("rustdoc does not allow disambiguating between `*const` and `*mut`, and pointers are unstable until it does") .emit(); } diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index 471ad73e2074..76f3663f04f2 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -109,7 +109,7 @@ impl Msrv { if let Some(duplicate) = msrv_attrs.last() { sess.dcx() .struct_span_err(duplicate.span, "`clippy::msrv` is defined multiple times") - .span_note(msrv_attr.span, "first definition found here") + .span_note_mv(msrv_attr.span, "first definition found here") .emit(); } diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index db80e07ca1c4..19d38903ade2 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -136,7 +136,7 @@ pub fn get_unique_attr<'a>( if let Some(duplicate) = unique_attr { sess.dcx() .struct_span_err(attr.span, format!("`{name}` is defined multiple times")) - .span_note(duplicate.span, "first definition found here") + .span_note_mv(duplicate.span, "first definition found here") .emit(); } else { unique_attr = Some(attr); diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs index 6bc53159b38b..7045a7dd9ce3 100644 --- a/src/tools/rustfmt/src/parse/parser.rs +++ b/src/tools/rustfmt/src/parse/parser.rs @@ -114,7 +114,7 @@ impl<'a> Parser<'a> { let mut parser = new_parser_from_file(sess.inner(), path, Some(span)); match parser.parse_mod(&TokenKind::Eof) { Ok((a, i, spans)) => Some((a, i, spans.inner_span)), - Err(mut e) => { + Err(e) => { e.emit(); if sess.can_reset_errors() { sess.reset_errors(); @@ -165,7 +165,7 @@ impl<'a> Parser<'a> { match catch_unwind(move || parser.parse_crate_mod()) { Ok(Ok(k)) => Ok(k), - Ok(Err(mut db)) => { + Ok(Err(db)) => { db.emit(); Err(ParserError::ParseError) } From 589591efde6c54baa8b7932ec3be6f45dc9d781f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 3 Jan 2024 16:00:29 +1100 Subject: [PATCH 202/257] Use chaining in `DiagnosticBuilder` construction. To avoid the use of a mutable local variable, and because it reads more nicely. --- .../rustc_attr/src/session_diagnostics.rs | 13 +- .../rustc_borrowck/src/borrowck_errors.rs | 70 +++---- .../src/diagnostics/conflict_errors.rs | 15 +- .../src/diagnostics/move_errors.rs | 31 ++- compiler/rustc_builtin_macros/src/errors.rs | 17 +- compiler/rustc_codegen_llvm/src/errors.rs | 14 +- compiler/rustc_codegen_ssa/src/errors.rs | 180 ++++++------------ .../rustc_errors/src/diagnostic_builder.rs | 4 +- compiler/rustc_errors/src/diagnostic_impls.rs | 63 +++--- compiler/rustc_errors/src/lib.rs | 40 +--- .../rustc_hir_analysis/src/check/wfcheck.rs | 6 +- compiler/rustc_hir_typeck/src/expr.rs | 8 +- .../src/infer/error_reporting/note.rs | 5 +- compiler/rustc_parse/src/lexer/mod.rs | 6 +- compiler/rustc_parse/src/parser/attr.rs | 27 ++- compiler/rustc_parse/src/parser/item.rs | 22 +-- compiler/rustc_parse/src/parser/pat.rs | 7 +- compiler/rustc_resolve/src/diagnostics.rs | 9 +- .../rustc_resolve/src/late/diagnostics.rs | 16 +- compiler/rustc_session/src/errors.rs | 7 +- compiler/rustc_symbol_mangling/src/errors.rs | 4 +- .../error_reporting/type_err_ctxt_ext.rs | 28 ++- 22 files changed, 223 insertions(+), 369 deletions(-) diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs index fdb5d66bf62b..5f61a56c189a 100644 --- a/compiler/rustc_attr/src/session_diagnostics.rs +++ b/compiler/rustc_attr/src/session_diagnostics.rs @@ -54,13 +54,12 @@ pub(crate) struct UnknownMetaItem<'a> { impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for UnknownMetaItem<'_> { fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> { let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::>(); - let mut diag = DiagnosticBuilder::new(dcx, level, fluent::attr_unknown_meta_item); - diag.span(self.span); - diag.code(error_code!(E0541)); - diag.arg("item", self.item); - diag.arg("expected", expected.join(", ")); - diag.span_label(self.span, fluent::attr_label); - diag + DiagnosticBuilder::new(dcx, level, fluent::attr_unknown_meta_item) + .span_mv(self.span) + .code_mv(error_code!(E0541)) + .arg_mv("item", self.item) + .arg_mv("expected", expected.join(", ")) + .span_label_mv(self.span, fluent::attr_label) } } diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 900b78910196..a9fc0a6f415d 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -31,17 +31,15 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { borrow_span: Span, borrow_desc: &str, ) -> DiagnosticBuilder<'tcx> { - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), span, E0503, "cannot use {} because it was mutably borrowed", desc, - ); - - err.span_label(borrow_span, format!("{borrow_desc} is borrowed here")); - err.span_label(span, format!("use of borrowed {borrow_desc}")); - err + ) + .span_label_mv(borrow_span, format!("{borrow_desc} is borrowed here")) + .span_label_mv(span, format!("use of borrowed {borrow_desc}")) } pub(crate) fn cannot_mutably_borrow_multiply( @@ -238,17 +236,15 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { borrow_span: Span, desc: &str, ) -> DiagnosticBuilder<'cx> { - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), span, E0506, "cannot assign to {} because it is borrowed", desc, - ); - - err.span_label(borrow_span, format!("{desc} is borrowed here")); - err.span_label(span, format!("{desc} is assigned to here but it was already borrowed")); - err + ) + .span_label_mv(borrow_span, format!("{desc} is borrowed here")) + .span_label_mv(span, format!("{desc} is assigned to here but it was already borrowed")) } pub(crate) fn cannot_reassign_immutable( @@ -287,16 +283,15 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { (&ty::Slice(_), _) => "slice", _ => span_bug!(move_from_span, "this path should not cause illegal move"), }; - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), move_from_span, E0508, "cannot move out of type `{}`, a non-copy {}", ty, type_name, - ); - err.span_label(move_from_span, "cannot move out of here"); - err + ) + .span_label_mv(move_from_span, "cannot move out of here") } pub(crate) fn cannot_move_out_of_interior_of_drop( @@ -304,15 +299,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { move_from_span: Span, container_ty: Ty<'_>, ) -> DiagnosticBuilder<'cx> { - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), move_from_span, E0509, "cannot move out of type `{}`, which implements the `Drop` trait", container_ty, - ); - err.span_label(move_from_span, "cannot move out of here"); - err + ) + .span_label_mv(move_from_span, "cannot move out of here") } pub(crate) fn cannot_act_on_moved_value( @@ -352,7 +346,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { immutable_section: &str, action: &str, ) -> DiagnosticBuilder<'tcx> { - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), mutate_span, E0510, @@ -360,10 +354,9 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { action, immutable_place, immutable_section, - ); - err.span_label(mutate_span, format!("cannot {action}")); - err.span_label(immutable_span, format!("value is immutable in {immutable_section}")); - err + ) + .span_label_mv(mutate_span, format!("cannot {action}")) + .span_label_mv(immutable_span, format!("value is immutable in {immutable_section}")) } pub(crate) fn cannot_borrow_across_coroutine_yield( @@ -372,14 +365,13 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { yield_span: Span, ) -> DiagnosticBuilder<'tcx> { let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind; - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), span, E0626, "borrow may still be in use when {coroutine_kind:#} yields", - ); - err.span_label(yield_span, "possible yield occurs here"); - err + ) + .span_label_mv(yield_span, "possible yield occurs here") } pub(crate) fn cannot_borrow_across_destructor( @@ -409,7 +401,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { reference_desc: &str, path_desc: &str, ) -> DiagnosticBuilder<'tcx> { - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), span, E0515, @@ -417,14 +409,11 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { RETURN = return_kind, REFERENCE = reference_desc, LOCAL = path_desc, - ); - - err.span_label( + ) + .span_label_mv( span, format!("{return_kind}s a {reference_desc} data owned by the current function"), - ); - - err + ) } pub(crate) fn cannot_capture_in_long_lived_closure( @@ -435,16 +424,15 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { capture_span: Span, scope: &str, ) -> DiagnosticBuilder<'tcx> { - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), closure_span, E0373, "{closure_kind} may outlive the current {scope}, but it borrows {borrowed_path}, \ which is owned by the current {scope}", - ); - err.span_label(capture_span, format!("{borrowed_path} is borrowed here")) - .span_label(closure_span, format!("may outlive borrowed value {borrowed_path}")); - err + ) + .span_label_mv(capture_span, format!("{borrowed_path} is borrowed here")) + .span_label_mv(closure_span, format!("may outlive borrowed value {borrowed_path}")) } pub(crate) fn thread_local_value_does_not_live_long_enough( diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9ce753134fbc..5baa108ed3cc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2218,15 +2218,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { drop_span, borrow_span ); - let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span); - - err.span_label( - borrow_span, - "thread-local variables cannot be borrowed beyond the end of the function", - ); - err.span_label(drop_span, "end of enclosing function is here"); - - err + self.thread_local_value_does_not_live_long_enough(borrow_span) + .span_label_mv( + borrow_span, + "thread-local variables cannot be borrowed beyond the end of the function", + ) + .span_label_mv(drop_span, "end of enclosing function is here") } #[instrument(level = "debug", skip(self))] diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index f3b21d22c1a5..50dde5ce636e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -329,15 +329,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let PlaceRef { local, projection: [] } = deref_base { let decl = &self.body.local_decls[local]; if decl.is_ref_for_guard() { - let mut err = self.cannot_move_out_of( - span, - &format!("`{}` in pattern guard", self.local_names[local].unwrap()), - ); - err.note( - "variables bound in patterns cannot be moved from \ + return self + .cannot_move_out_of( + span, + &format!("`{}` in pattern guard", self.local_names[local].unwrap()), + ) + .note_mv( + "variables bound in patterns cannot be moved from \ until after the end of the pattern guard", - ); - return err; + ); } else if decl.is_ref_to_static() { return self.report_cannot_move_from_static(move_place, span); } @@ -381,15 +381,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { closure_kind_ty, closure_kind, place_description, ); - let mut diag = self.cannot_move_out_of(span, &place_description); - - diag.span_label(upvar_span, "captured outer variable"); - diag.span_label( - self.infcx.tcx.def_span(def_id), - format!("captured by this `{closure_kind}` closure"), - ); - - diag + self.cannot_move_out_of(span, &place_description) + .span_label_mv(upvar_span, "captured outer variable") + .span_label_mv( + self.infcx.tcx.def_span(def_id), + format!("captured by this `{closure_kind}` closure"), + ) } _ => { let source = self.borrowed_content_source(deref_base); diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index f9ddffcc155d..13e5a40ca9ee 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -803,24 +803,23 @@ pub(crate) struct AsmClobberNoReg { impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for AsmClobberNoReg { fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> { - let mut diag = DiagnosticBuilder::new( - dcx, - level, - crate::fluent_generated::builtin_macros_asm_clobber_no_reg, - ); - diag.span(self.spans.clone()); // eager translation as `span_labels` takes `AsRef` let lbl1 = dcx.eagerly_translate_to_string( crate::fluent_generated::builtin_macros_asm_clobber_abi, [].into_iter(), ); - diag.span_labels(self.clobbers, &lbl1); let lbl2 = dcx.eagerly_translate_to_string( crate::fluent_generated::builtin_macros_asm_clobber_outputs, [].into_iter(), ); - diag.span_labels(self.spans, &lbl2); - diag + DiagnosticBuilder::new( + dcx, + level, + crate::fluent_generated::builtin_macros_asm_clobber_no_reg, + ) + .span_mv(self.spans.clone()) + .span_labels_mv(self.clobbers, &lbl1) + .span_labels_mv(self.spans, &lbl2) } } diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 422e8edff5f6..6e2c0dc21ad8 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -105,10 +105,8 @@ impl IntoDiagnostic<'_, G> for ParseTargetMachineConfig<'_ let (message, _) = diag.messages().first().expect("`LlvmError` with no message"); let message = dcx.eagerly_translate_to_string(message.clone(), diag.args()); - let mut diag = - DiagnosticBuilder::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config); - diag.arg("error", message); - diag + DiagnosticBuilder::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config) + .arg_mv("error", message) } } @@ -204,10 +202,10 @@ impl IntoDiagnostic<'_, G> for WithLlvmError<'_> { PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err, ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err, }; - let mut diag = self.0.into_diagnostic(dcx, level); - diag.primary_message(msg_with_llvm_err); - diag.arg("llvm_err", self.1); - diag + self.0 + .into_diagnostic(dcx, level) + .primary_message_mv(msg_with_llvm_err) + .arg_mv("llvm_err", self.1) } } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index c1086bebb8d4..7e3b69fa52f4 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -212,192 +212,124 @@ pub struct ThorinErrorWrapper(pub thorin::Error); impl IntoDiagnostic<'_, G> for ThorinErrorWrapper { fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> { let build = |msg| DiagnosticBuilder::new(dcx, level, msg); - let mut diag; match self.0 { - thorin::Error::ReadInput(_) => { - diag = build(fluent::codegen_ssa_thorin_read_input_failure); - diag - } + thorin::Error::ReadInput(_) => build(fluent::codegen_ssa_thorin_read_input_failure), thorin::Error::ParseFileKind(_) => { - diag = build(fluent::codegen_ssa_thorin_parse_input_file_kind); - diag + build(fluent::codegen_ssa_thorin_parse_input_file_kind) } thorin::Error::ParseObjectFile(_) => { - diag = build(fluent::codegen_ssa_thorin_parse_input_object_file); - diag + build(fluent::codegen_ssa_thorin_parse_input_object_file) } thorin::Error::ParseArchiveFile(_) => { - diag = build(fluent::codegen_ssa_thorin_parse_input_archive_file); - diag + build(fluent::codegen_ssa_thorin_parse_input_archive_file) } thorin::Error::ParseArchiveMember(_) => { - diag = build(fluent::codegen_ssa_thorin_parse_archive_member); - diag - } - thorin::Error::InvalidInputKind => { - diag = build(fluent::codegen_ssa_thorin_invalid_input_kind); - diag - } - thorin::Error::DecompressData(_) => { - diag = build(fluent::codegen_ssa_thorin_decompress_data); - diag + build(fluent::codegen_ssa_thorin_parse_archive_member) } + thorin::Error::InvalidInputKind => build(fluent::codegen_ssa_thorin_invalid_input_kind), + thorin::Error::DecompressData(_) => build(fluent::codegen_ssa_thorin_decompress_data), thorin::Error::NamelessSection(_, offset) => { - diag = build(fluent::codegen_ssa_thorin_section_without_name); - diag.arg("offset", format!("0x{offset:08x}")); - diag + build(fluent::codegen_ssa_thorin_section_without_name) + .arg_mv("offset", format!("0x{offset:08x}")) } thorin::Error::RelocationWithInvalidSymbol(section, offset) => { - diag = build(fluent::codegen_ssa_thorin_relocation_with_invalid_symbol); - diag.arg("section", section); - diag.arg("offset", format!("0x{offset:08x}")); - diag + build(fluent::codegen_ssa_thorin_relocation_with_invalid_symbol) + .arg_mv("section", section) + .arg_mv("offset", format!("0x{offset:08x}")) } thorin::Error::MultipleRelocations(section, offset) => { - diag = build(fluent::codegen_ssa_thorin_multiple_relocations); - diag.arg("section", section); - diag.arg("offset", format!("0x{offset:08x}")); - diag + build(fluent::codegen_ssa_thorin_multiple_relocations) + .arg_mv("section", section) + .arg_mv("offset", format!("0x{offset:08x}")) } thorin::Error::UnsupportedRelocation(section, offset) => { - diag = build(fluent::codegen_ssa_thorin_unsupported_relocation); - diag.arg("section", section); - diag.arg("offset", format!("0x{offset:08x}")); - diag - } - thorin::Error::MissingDwoName(id) => { - diag = build(fluent::codegen_ssa_thorin_missing_dwo_name); - diag.arg("id", format!("0x{id:08x}")); - diag + build(fluent::codegen_ssa_thorin_unsupported_relocation) + .arg_mv("section", section) + .arg_mv("offset", format!("0x{offset:08x}")) } + thorin::Error::MissingDwoName(id) => build(fluent::codegen_ssa_thorin_missing_dwo_name) + .arg_mv("id", format!("0x{id:08x}")), thorin::Error::NoCompilationUnits => { - diag = build(fluent::codegen_ssa_thorin_no_compilation_units); - diag - } - thorin::Error::NoDie => { - diag = build(fluent::codegen_ssa_thorin_no_die); - diag + build(fluent::codegen_ssa_thorin_no_compilation_units) } + thorin::Error::NoDie => build(fluent::codegen_ssa_thorin_no_die), thorin::Error::TopLevelDieNotUnit => { - diag = build(fluent::codegen_ssa_thorin_top_level_die_not_unit); - diag + build(fluent::codegen_ssa_thorin_top_level_die_not_unit) } thorin::Error::MissingRequiredSection(section) => { - diag = build(fluent::codegen_ssa_thorin_missing_required_section); - diag.arg("section", section); - diag + build(fluent::codegen_ssa_thorin_missing_required_section) + .arg_mv("section", section) } thorin::Error::ParseUnitAbbreviations(_) => { - diag = build(fluent::codegen_ssa_thorin_parse_unit_abbreviations); - diag + build(fluent::codegen_ssa_thorin_parse_unit_abbreviations) } thorin::Error::ParseUnitAttribute(_) => { - diag = build(fluent::codegen_ssa_thorin_parse_unit_attribute); - diag + build(fluent::codegen_ssa_thorin_parse_unit_attribute) } thorin::Error::ParseUnitHeader(_) => { - diag = build(fluent::codegen_ssa_thorin_parse_unit_header); - diag - } - thorin::Error::ParseUnit(_) => { - diag = build(fluent::codegen_ssa_thorin_parse_unit); - diag + build(fluent::codegen_ssa_thorin_parse_unit_header) } + thorin::Error::ParseUnit(_) => build(fluent::codegen_ssa_thorin_parse_unit), thorin::Error::IncompatibleIndexVersion(section, format, actual) => { - diag = build(fluent::codegen_ssa_thorin_incompatible_index_version); - diag.arg("section", section); - diag.arg("actual", actual); - diag.arg("format", format); - diag + build(fluent::codegen_ssa_thorin_incompatible_index_version) + .arg_mv("section", section) + .arg_mv("actual", actual) + .arg_mv("format", format) } thorin::Error::OffsetAtIndex(_, index) => { - diag = build(fluent::codegen_ssa_thorin_offset_at_index); - diag.arg("index", index); - diag + build(fluent::codegen_ssa_thorin_offset_at_index).arg_mv("index", index) } thorin::Error::StrAtOffset(_, offset) => { - diag = build(fluent::codegen_ssa_thorin_str_at_offset); - diag.arg("offset", format!("0x{offset:08x}")); - diag + build(fluent::codegen_ssa_thorin_str_at_offset) + .arg_mv("offset", format!("0x{offset:08x}")) } thorin::Error::ParseIndex(_, section) => { - diag = build(fluent::codegen_ssa_thorin_parse_index); - diag.arg("section", section); - diag + build(fluent::codegen_ssa_thorin_parse_index).arg_mv("section", section) } thorin::Error::UnitNotInIndex(unit) => { - diag = build(fluent::codegen_ssa_thorin_unit_not_in_index); - diag.arg("unit", format!("0x{unit:08x}")); - diag + build(fluent::codegen_ssa_thorin_unit_not_in_index) + .arg_mv("unit", format!("0x{unit:08x}")) } thorin::Error::RowNotInIndex(_, row) => { - diag = build(fluent::codegen_ssa_thorin_row_not_in_index); - diag.arg("row", row); - diag - } - thorin::Error::SectionNotInRow => { - diag = build(fluent::codegen_ssa_thorin_section_not_in_row); - diag + build(fluent::codegen_ssa_thorin_row_not_in_index).arg_mv("row", row) } + thorin::Error::SectionNotInRow => build(fluent::codegen_ssa_thorin_section_not_in_row), thorin::Error::EmptyUnit(unit) => { - diag = build(fluent::codegen_ssa_thorin_empty_unit); - diag.arg("unit", format!("0x{unit:08x}")); - diag + build(fluent::codegen_ssa_thorin_empty_unit).arg_mv("unit", format!("0x{unit:08x}")) } thorin::Error::MultipleDebugInfoSection => { - diag = build(fluent::codegen_ssa_thorin_multiple_debug_info_section); - diag + build(fluent::codegen_ssa_thorin_multiple_debug_info_section) } thorin::Error::MultipleDebugTypesSection => { - diag = build(fluent::codegen_ssa_thorin_multiple_debug_types_section); - diag - } - thorin::Error::NotSplitUnit => { - diag = build(fluent::codegen_ssa_thorin_not_split_unit); - diag - } - thorin::Error::DuplicateUnit(unit) => { - diag = build(fluent::codegen_ssa_thorin_duplicate_unit); - diag.arg("unit", format!("0x{unit:08x}")); - diag + build(fluent::codegen_ssa_thorin_multiple_debug_types_section) } + thorin::Error::NotSplitUnit => build(fluent::codegen_ssa_thorin_not_split_unit), + thorin::Error::DuplicateUnit(unit) => build(fluent::codegen_ssa_thorin_duplicate_unit) + .arg_mv("unit", format!("0x{unit:08x}")), thorin::Error::MissingReferencedUnit(unit) => { - diag = build(fluent::codegen_ssa_thorin_missing_referenced_unit); - diag.arg("unit", format!("0x{unit:08x}")); - diag + build(fluent::codegen_ssa_thorin_missing_referenced_unit) + .arg_mv("unit", format!("0x{unit:08x}")) } thorin::Error::NoOutputObjectCreated => { - diag = build(fluent::codegen_ssa_thorin_not_output_object_created); - diag + build(fluent::codegen_ssa_thorin_not_output_object_created) } thorin::Error::MixedInputEncodings => { - diag = build(fluent::codegen_ssa_thorin_mixed_input_encodings); - diag + build(fluent::codegen_ssa_thorin_mixed_input_encodings) } thorin::Error::Io(e) => { - diag = build(fluent::codegen_ssa_thorin_io); - diag.arg("error", format!("{e}")); - diag + build(fluent::codegen_ssa_thorin_io).arg_mv("error", format!("{e}")) } thorin::Error::ObjectRead(e) => { - diag = build(fluent::codegen_ssa_thorin_object_read); - diag.arg("error", format!("{e}")); - diag + build(fluent::codegen_ssa_thorin_object_read).arg_mv("error", format!("{e}")) } thorin::Error::ObjectWrite(e) => { - diag = build(fluent::codegen_ssa_thorin_object_write); - diag.arg("error", format!("{e}")); - diag + build(fluent::codegen_ssa_thorin_object_write).arg_mv("error", format!("{e}")) } thorin::Error::GimliRead(e) => { - diag = build(fluent::codegen_ssa_thorin_gimli_read); - diag.arg("error", format!("{e}")); - diag + build(fluent::codegen_ssa_thorin_gimli_read).arg_mv("error", format!("{e}")) } thorin::Error::GimliWrite(e) => { - diag = build(fluent::codegen_ssa_thorin_gimli_write); - diag.arg("error", format!("{e}")); - diag + build(fluent::codegen_ssa_thorin_gimli_write).arg_mv("error", format!("{e}")) } _ => unimplemented!("Untranslated thorin error"), } diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index e41acd6447bf..b3f8a6c24407 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -30,9 +30,7 @@ where G: EmissionGuarantee, { fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> { - let mut diag = self.node.into_diagnostic(dcx, level); - diag.span(self.span); - diag + self.node.into_diagnostic(dcx, level).span_mv(self.span) } } diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index de27c6e910b2..58d4d2caf2ee 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -249,60 +249,43 @@ impl IntoDiagnosticArg for hir::def::Res { impl IntoDiagnostic<'_, G> for TargetDataLayoutErrors<'_> { fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> { - let mut diag; match self { TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => { - diag = - DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_address_space); - diag.arg("addr_space", addr_space); - diag.arg("cause", cause); - diag.arg("err", err); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_address_space) + .arg_mv("addr_space", addr_space) + .arg_mv("cause", cause) + .arg_mv("err", err) } TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => { - diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits); - diag.arg("kind", kind); - diag.arg("bit", bit); - diag.arg("cause", cause); - diag.arg("err", err); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits) + .arg_mv("kind", kind) + .arg_mv("bit", bit) + .arg_mv("cause", cause) + .arg_mv("err", err) } TargetDataLayoutErrors::MissingAlignment { cause } => { - diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_missing_alignment); - diag.arg("cause", cause); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_missing_alignment) + .arg_mv("cause", cause) } TargetDataLayoutErrors::InvalidAlignment { cause, err } => { - diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_alignment); - diag.arg("cause", cause); - diag.arg("err_kind", err.diag_ident()); - diag.arg("align", err.align()); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_alignment) + .arg_mv("cause", cause) + .arg_mv("err_kind", err.diag_ident()) + .arg_mv("align", err.align()) } TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { - diag = DiagnosticBuilder::new( - dcx, - level, - fluent::errors_target_inconsistent_architecture, - ); - diag.arg("dl", dl); - diag.arg("target", target); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_inconsistent_architecture) + .arg_mv("dl", dl) + .arg_mv("target", target) } TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => { - diag = DiagnosticBuilder::new( - dcx, - level, - fluent::errors_target_inconsistent_pointer_width, - ); - diag.arg("pointer_size", pointer_size); - diag.arg("target", target); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_inconsistent_pointer_width) + .arg_mv("pointer_size", pointer_size) + .arg_mv("target", target) } TargetDataLayoutErrors::InvalidBitsSize { err } => { - diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits_size); - diag.arg("err", err); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits_size) + .arg_mv("err", err) } } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index e07a9509383b..a63c32566ca7 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -729,9 +729,7 @@ impl DiagCtxt { span: impl Into, msg: impl Into, ) -> DiagnosticBuilder<'_, ()> { - let mut result = self.struct_warn(msg); - result.span(span); - result + self.struct_warn(msg).span_mv(span) } /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. @@ -744,9 +742,7 @@ impl DiagCtxt { msg: impl Into, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ()> { - let mut result = self.struct_span_warn(span, msg); - result.code(code); - result + self.struct_span_warn(span, msg).code_mv(code) } /// Construct a builder at the `Warning` level with the `msg`. @@ -786,9 +782,7 @@ impl DiagCtxt { span: impl Into, msg: impl Into, ) -> DiagnosticBuilder<'_> { - let mut result = self.struct_err(msg); - result.span(span); - result + self.struct_err(msg).span_mv(span) } /// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`. @@ -800,9 +794,7 @@ impl DiagCtxt { msg: impl Into, code: DiagnosticId, ) -> DiagnosticBuilder<'_> { - let mut result = self.struct_span_err(span, msg); - result.code(code); - result + self.struct_span_err(span, msg).code_mv(code) } /// Construct a builder at the `Error` level with the `msg`. @@ -821,9 +813,7 @@ impl DiagCtxt { msg: impl Into, code: DiagnosticId, ) -> DiagnosticBuilder<'_> { - let mut result = self.struct_err(msg); - result.code(code); - result + self.struct_err(msg).code_mv(code) } /// Construct a builder at the `Warn` level with the `msg` and the `code`. @@ -834,9 +824,7 @@ impl DiagCtxt { msg: impl Into, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ()> { - let mut result = self.struct_warn(msg); - result.code(code); - result + self.struct_warn(msg).code_mv(code) } /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`. @@ -847,9 +835,7 @@ impl DiagCtxt { span: impl Into, msg: impl Into, ) -> DiagnosticBuilder<'_, FatalAbort> { - let mut result = self.struct_fatal(msg); - result.span(span); - result + self.struct_fatal(msg).span_mv(span) } /// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`. @@ -861,9 +847,7 @@ impl DiagCtxt { msg: impl Into, code: DiagnosticId, ) -> DiagnosticBuilder<'_, FatalAbort> { - let mut result = self.struct_span_fatal(span, msg); - result.code(code); - result + self.struct_span_fatal(span, msg).code_mv(code) } /// Construct a builder at the `Fatal` level with the `msg`. @@ -904,9 +888,7 @@ impl DiagCtxt { span: impl Into, msg: impl Into, ) -> DiagnosticBuilder<'_, BugAbort> { - let mut result = self.struct_bug(msg); - result.span(span); - result + self.struct_bug(msg).span_mv(span) } #[rustc_lint_diagnostics] @@ -1021,9 +1003,7 @@ impl DiagCtxt { span: impl Into, msg: impl Into, ) -> DiagnosticBuilder<'_, ()> { - let mut db = DiagnosticBuilder::new(self, Note, msg); - db.span(span); - db + DiagnosticBuilder::new(self, Note, msg).span_mv(span) } #[rustc_lint_diagnostics] diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c53ff5655500..4bac124b58bb 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1921,10 +1921,8 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error } fn error_392(tcx: TyCtxt<'_>, span: Span, param_name: Symbol) -> DiagnosticBuilder<'_> { - let mut err = - struct_span_err!(tcx.dcx(), span, E0392, "parameter `{param_name}` is never used"); - err.span_label(span, "unused parameter"); - err + struct_span_err!(tcx.dcx(), span, E0392, "parameter `{param_name}` is never used") + .span_label_mv(span, "unused parameter") } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 69a65e8b750b..da9d41a7d72d 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2836,15 +2836,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn private_field_err(&self, field: Ident, base_did: DefId) -> DiagnosticBuilder<'_> { let struct_path = self.tcx().def_path_str(base_did); let kind_name = self.tcx().def_descr(base_did); - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), field.span, E0616, "field `{field}` of {kind_name} `{struct_path}` is private", - ); - err.span_label(field.span, "private field"); - - err + ) + .span_label_mv(field.span, "private field") } pub(crate) fn get_field_candidates_considering_privacy( diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index e9fa39ad08a2..c09646b491c0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -367,9 +367,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &trace.cause.code().peel_derives() { let span = *span; - let mut err = self.report_concrete_failure(placeholder_origin, sub, sup); - err.span_note(span, "the lifetime requirement is introduced here"); - err + self.report_concrete_failure(placeholder_origin, sub, sup) + .span_note_mv(span, "the lifetime requirement is introduced here") } else { unreachable!( "control flow ensures we have a `BindingObligation` or `ExprBindingObligation` here..." diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 4819ed6021d0..bc42b1b3ef84 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -249,9 +249,9 @@ impl<'a> StringReader<'a> { let lifetime_name = self.str_from(start); if starts_with_number { let span = self.mk_sp(start, self.pos); - let mut diag = self.dcx().struct_err("lifetimes cannot start with a number"); - diag.span(span); - diag.stash(span, StashKey::LifetimeIsChar); + self.dcx().struct_err("lifetimes cannot start with a number") + .span_mv(span) + .stash(span, StashKey::LifetimeIsChar); } let ident = Symbol::intern(lifetime_name); token::Lifetime(ident) diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index c8629069968a..1f8c368b2a99 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -200,23 +200,22 @@ impl<'a> Parser<'a> { if let InnerAttrPolicy::Forbidden(reason) = policy { let mut diag = match reason.as_ref().copied() { Some(InnerAttrForbiddenReason::AfterOuterDocComment { prev_doc_comment_span }) => { - let mut diag = self.dcx().struct_span_err( - attr_sp, - fluent::parse_inner_attr_not_permitted_after_outer_doc_comment, - ); - diag.span_label(attr_sp, fluent::parse_label_attr) - .span_label(prev_doc_comment_span, fluent::parse_label_prev_doc_comment); - diag + self.dcx() + .struct_span_err( + attr_sp, + fluent::parse_inner_attr_not_permitted_after_outer_doc_comment, + ) + .span_label_mv(attr_sp, fluent::parse_label_attr) + .span_label_mv(prev_doc_comment_span, fluent::parse_label_prev_doc_comment) } - Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => { - let mut diag = self.dcx().struct_span_err( + Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => self + .dcx() + .struct_span_err( attr_sp, fluent::parse_inner_attr_not_permitted_after_outer_attr, - ); - diag.span_label(attr_sp, fluent::parse_label_attr) - .span_label(prev_outer_attr_sp, fluent::parse_label_prev_attr); - diag - } + ) + .span_label_mv(attr_sp, fluent::parse_label_attr) + .span_label_mv(prev_outer_attr_sp, fluent::parse_label_prev_attr), Some(InnerAttrForbiddenReason::InCodeBlock) | None => { self.dcx().struct_span_err(attr_sp, fluent::parse_inner_attr_not_permitted) } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 45aafbd0fdf3..2694dd3c9c61 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1941,15 +1941,14 @@ impl<'a> Parser<'a> { Case::Insensitive, ) { Ok(_) => { - let mut err = self.dcx().struct_span_err( + self.dcx().struct_span_err( lo.to(self.prev_token.span), format!("functions are not allowed in {adt_ty} definitions"), - ); - err.help( + ) + .help_mv( "unlike in C++, Java, and C#, functions are declared in `impl` blocks", - ); - err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information"); - err + ) + .help_mv("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information") } Err(err) => { err.cancel(); @@ -1959,14 +1958,13 @@ impl<'a> Parser<'a> { } } else if self.eat_keyword(kw::Struct) { match self.parse_item_struct() { - Ok((ident, _)) => { - let mut err = self.dcx().struct_span_err( + Ok((ident, _)) => self + .dcx() + .struct_span_err( lo.with_hi(ident.span.hi()), format!("structs are not allowed in {adt_ty} definitions"), - ); - err.help("consider creating a new `struct` definition instead of nesting"); - err - } + ) + .help_mv("consider creating a new `struct` definition instead of nesting"), Err(err) => { err.cancel(); self.restore_snapshot(snapshot); diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index e9b68a129ef6..a6804b992043 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -460,9 +460,10 @@ impl<'a> Parser<'a> { super::token_descr(&self_.token) ); - let mut err = self_.dcx().struct_span_err(self_.token.span, msg); - err.span_label(self_.token.span, format!("expected {expected}")); - err + self_ + .dcx() + .struct_span_err(self_.token.span, msg) + .span_label_mv(self_.token.span, format!("expected {expected}")) }); PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit))) } else { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index c1cb20f6bb09..977a7e796e78 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -944,16 +944,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { trait_item_span, trait_path, } => { - let mut err = self.dcx().struct_span_err_with_code( + self.dcx().struct_span_err_with_code( span, format!( "item `{name}` is an associated {kind}, which doesn't match its trait `{trait_path}`", ), code, - ); - err.span_label(span, "does not match trait"); - err.span_label(trait_item_span, "item in trait"); - err + ) + .span_label_mv(span, "does not match trait") + .span_label_mv(trait_item_span, "item in trait") } ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => self .dcx() diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 821aafe2feda..43608da8ab2a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2603,25 +2603,23 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ) { debug_assert_ne!(lifetime_ref.ident.name, kw::UnderscoreLifetime); let mut err = if let Some(outer) = outer_lifetime_ref { - let mut err = struct_span_err!( + struct_span_err!( self.r.dcx(), lifetime_ref.ident.span, E0401, "can't use generic parameters from outer item", - ); - err.span_label(lifetime_ref.ident.span, "use of generic parameter from outer item"); - err.span_label(outer.span, "lifetime parameter from outer item"); - err + ) + .span_label_mv(lifetime_ref.ident.span, "use of generic parameter from outer item") + .span_label_mv(outer.span, "lifetime parameter from outer item") } else { - let mut err = struct_span_err!( + struct_span_err!( self.r.dcx(), lifetime_ref.ident.span, E0261, "use of undeclared lifetime name `{}`", lifetime_ref.ident - ); - err.span_label(lifetime_ref.ident.span, "undeclared lifetime"); - err + ) + .span_label_mv(lifetime_ref.ident.span, "undeclared lifetime") }; self.suggest_introducing_lifetime( &mut err, diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 758c31224045..8baa5d892a5c 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -18,10 +18,9 @@ pub struct FeatureGateError { impl<'a> IntoDiagnostic<'a> for FeatureGateError { #[track_caller] fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a> { - let mut diag = DiagnosticBuilder::new(dcx, level, self.explain); - diag.span(self.span); - diag.code(error_code!(E0658)); - diag + DiagnosticBuilder::new(dcx, level, self.explain) + .span_mv(self.span) + .code_mv(error_code!(E0658)) } } diff --git a/compiler/rustc_symbol_mangling/src/errors.rs b/compiler/rustc_symbol_mangling/src/errors.rs index 0fa59d2ddfba..746783ab7e3e 100644 --- a/compiler/rustc_symbol_mangling/src/errors.rs +++ b/compiler/rustc_symbol_mangling/src/errors.rs @@ -18,9 +18,7 @@ impl IntoDiagnostic<'_, G> for TestOutput { let TestOutput { span, kind, content } = self; #[allow(rustc::untranslatable_diagnostic)] - let mut diag = DiagnosticBuilder::new(dcx, level, format!("{kind}({content})")); - diag.span(span); - diag + DiagnosticBuilder::new(dcx, level, format!("{kind}({content})")).span_mv(span) } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index efc75ee538e8..6bf1307b37f3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -2654,26 +2654,24 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .chain(Some(data.term.into_arg())) .find(|g| g.has_non_region_infer()); if let Some(subst) = subst { - let mut err = self.emit_inference_failure_err( + self.emit_inference_failure_err( obligation.cause.body_id, span, subst, ErrorCode::E0284, true, - ); - err.note(format!("cannot satisfy `{predicate}`")); - err + ) + .note_mv(format!("cannot satisfy `{predicate}`")) } else { // If we can't find a substitution, just print a generic error - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), span, E0284, "type annotations needed: cannot satisfy `{}`", predicate, - ); - err.span_label(span, format!("cannot satisfy `{predicate}`")); - err + ) + .span_label_mv(span, format!("cannot satisfy `{predicate}`")) } } @@ -2693,30 +2691,28 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err } else { // If we can't find a substitution, just print a generic error - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), span, E0284, "type annotations needed: cannot satisfy `{}`", predicate, - ); - err.span_label(span, format!("cannot satisfy `{predicate}`")); - err + ) + .span_label_mv(span, format!("cannot satisfy `{predicate}`")) } } _ => { if self.dcx().has_errors().is_some() || self.tainted_by_errors().is_some() { return; } - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), span, E0284, "type annotations needed: cannot satisfy `{}`", predicate, - ); - err.span_label(span, format!("cannot satisfy `{predicate}`")); - err + ) + .span_label_mv(span, format!("cannot satisfy `{predicate}`")) } }; self.note_obligation_cause(&mut err, obligation); From bd4e623485f383b478fae662a767a3129bb4b989 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 3 Jan 2024 17:03:10 +1100 Subject: [PATCH 203/257] Use chaining for `DiagnosticBuilder` construction and `emit`. To avoid the use of a mutable local variable, and because it reads more nicely. --- compiler/rustc_builtin_macros/src/asm.rs | 15 ++-- .../rustc_codegen_cranelift/src/driver/jit.rs | 7 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 8 +- .../src/astconv/generics.rs | 6 +- .../src/astconv/object_safety.rs | 10 +-- .../rustc_hir_analysis/src/check/check.rs | 6 +- .../src/check/intrinsicck.rs | 74 +++++++++++-------- .../rustc_hir_analysis/src/check/wfcheck.rs | 53 +++++++------ .../src/coherence/inherent_impls_overlap.rs | 11 ++- .../src/collect/resolve_bound_vars.rs | 8 +- compiler/rustc_hir_typeck/src/expr.rs | 33 +++++---- compiler/rustc_hir_typeck/src/pat.rs | 10 +-- compiler/rustc_hir_typeck/src/place_op.rs | 12 +-- compiler/rustc_middle/src/lint.rs | 1 - compiler/rustc_middle/src/values.rs | 10 +-- .../rustc_parse/src/parser/diagnostics.rs | 19 ++--- compiler/rustc_parse/src/parser/generics.rs | 23 +++--- compiler/rustc_parse/src/parser/item.rs | 3 +- .../rustc_resolve/src/late/diagnostics.rs | 25 ++++--- compiler/rustc_resolve/src/macros.rs | 9 +-- .../error_reporting/type_err_ctxt_ext.rs | 21 +++--- src/librustdoc/core.rs | 12 +-- 22 files changed, 193 insertions(+), 183 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index ac90f4d4084d..d0e13fa3c0f3 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -693,13 +693,14 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option {} 1 => { let (sp, msg) = unused_operands.into_iter().next().unwrap(); - let mut err = ecx.dcx().struct_span_err(sp, msg); - err.span_label(sp, msg); - err.help(format!( - "if this argument is intentionally unused, \ - consider using it in an asm comment: `\"/*{help_str} */\"`" - )); - err.emit(); + ecx.dcx() + .struct_span_err(sp, msg) + .span_label_mv(sp, msg) + .help_mv(format!( + "if this argument is intentionally unused, \ + consider using it in an asm comment: `\"/*{help_str} */\"`" + )) + .emit(); } _ => { let mut err = ecx.dcx().struct_span_err( diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index a8d8fb189e2e..50d9f287e74c 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -321,9 +321,10 @@ fn dep_symbol_lookup_fn( Linkage::NotLinked | Linkage::IncludedFromDylib => {} Linkage::Static => { let name = crate_info.crate_name[&cnum]; - let mut err = sess.dcx().struct_err(format!("Can't load static lib {}", name)); - err.note("rustc_codegen_cranelift can only load dylibs in JIT mode."); - err.emit(); + sess.dcx() + .struct_err(format!("Can't load static lib {}", name)) + .note("rustc_codegen_cranelift can only load dylibs in JIT mode.") + .emit(); } Linkage::Dynamic => { dylib_paths.push(src.dylib.as_ref().unwrap().0.clone()); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index a1ec191f1052..f53067d194ac 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -303,14 +303,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // This exception needs to be kept in sync with allowing // `#[target_feature]` on `main` and `start`. } else if !tcx.features().target_feature_11 { - let mut err = feature_err( + feature_err( &tcx.sess.parse_sess, sym::target_feature_11, attr.span, "`#[target_feature(..)]` can only be applied to `unsafe` functions", - ); - err.span_label(tcx.def_span(did), "not an `unsafe` function"); - err.emit(); + ) + .span_label_mv(tcx.def_span(did), "not an `unsafe` function") + .emit(); } else { check_target_feature_trait_unsafe(tcx, did, attr.span); } diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index 257b108471f4..adc6a9de8086 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -650,9 +650,9 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( if position == GenericArgPosition::Value && args.num_lifetime_params() != param_counts.lifetimes { - let mut err = struct_span_err!(tcx.dcx(), span, E0794, "{}", msg); - err.span_note(span_late, note); - err.emit(); + struct_span_err!(tcx.dcx(), span, E0794, "{}", msg) + .span_note_mv(span_late, note) + .emit(); } else { let mut multispan = MultiSpan::from_span(span); multispan.push_span_label(span_late, note); diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs index ff84a9824952..703e0bdc40ed 100644 --- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs @@ -290,19 +290,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if references_self { let def_id = i.bottom().0.def_id(); - let mut err = struct_span_err!( + struct_span_err!( tcx.dcx(), i.bottom().1, E0038, "the {} `{}` cannot be made into an object", tcx.def_descr(def_id), tcx.item_name(def_id), - ); - err.note( + ) + .note_mv( rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![]) .error_msg(), - ); - err.emit(); + ) + .emit(); } ty::ExistentialTraitRef { def_id: trait_ref.def_id, args } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 544e06879f6f..4b26a469eb56 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1140,13 +1140,13 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { let disr_non_unit = def.variants().iter().any(|var| !is_unit(var) && has_disr(var)); if disr_non_unit || (disr_units && has_non_units) { - let err = struct_span_err!( + struct_span_err!( tcx.dcx(), tcx.def_span(def_id), E0732, "`#[repr(inttype)]` must be specified" - ); - err.emit(); + ) + .emit(); } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index ac18e6de0bac..1979f52eda94 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -153,12 +153,14 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { }; let Some(asm_ty) = asm_ty else { let msg = format!("cannot use value of type `{ty}` for inline assembly"); - let mut err = self.tcx.dcx().struct_span_err(expr.span, msg); - err.note( - "only integers, floats, SIMD vectors, pointers and function pointers \ - can be used as arguments for inline assembly", - ); - err.emit(); + self.tcx + .dcx() + .struct_span_err(expr.span, msg) + .note_mv( + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ) + .emit(); return None; }; @@ -166,9 +168,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // possibly fail is for SIMD types which don't #[derive(Copy)]. if !ty.is_copy_modulo_regions(self.tcx, self.param_env) { let msg = "arguments for inline assembly must be copyable"; - let mut err = self.tcx.dcx().struct_span_err(expr.span, msg); - err.note(format!("`{ty}` does not implement the Copy trait")); - err.emit(); + self.tcx + .dcx() + .struct_span_err(expr.span, msg) + .note_mv(format!("`{ty}` does not implement the Copy trait")) + .emit(); } // Ideally we wouldn't need to do this, but LLVM's register allocator @@ -183,16 +187,17 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if let Some((in_expr, Some(in_asm_ty))) = tied_input { if in_asm_ty != asm_ty { let msg = "incompatible types for asm inout argument"; - let mut err = self.tcx.dcx().struct_span_err(vec![in_expr.span, expr.span], msg); - let in_expr_ty = (self.get_operand_ty)(in_expr); - err.span_label(in_expr.span, format!("type `{in_expr_ty}`")); - err.span_label(expr.span, format!("type `{ty}`")); - err.note( - "asm inout arguments must have the same type, \ + self.tcx + .dcx() + .struct_span_err(vec![in_expr.span, expr.span], msg) + .span_label_mv(in_expr.span, format!("type `{in_expr_ty}`")) + .span_label_mv(expr.span, format!("type `{ty}`")) + .note_mv( + "asm inout arguments must have the same type, \ unless they are both pointers or integers of the same size", - ); - err.emit(); + ) + .emit(); } // All of the later checks have already been done on the input, so @@ -234,13 +239,15 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if let Some(feature) = feature { if !target_features.contains(feature) { let msg = format!("`{feature}` target feature is not enabled"); - let mut err = self.tcx.dcx().struct_span_err(expr.span, msg); - err.note(format!( - "this is required to use type `{}` with register class `{}`", - ty, - reg_class.name(), - )); - err.emit(); + self.tcx + .dcx() + .struct_span_err(expr.span, msg) + .note_mv(format!( + "this is required to use type `{}` with register class `{}`", + ty, + reg_class.name(), + )) + .emit(); return Some(asm_ty); } } @@ -449,14 +456,17 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Never | ty::Error(_) => {} ty::FnDef(..) => {} _ => { - let mut err = - self.tcx.dcx().struct_span_err(*op_sp, "invalid `sym` operand"); - err.span_label( - self.tcx.def_span(anon_const.def_id), - format!("is {} `{}`", ty.kind().article(), ty), - ); - err.help("`sym` operands must refer to either a function or a static"); - err.emit(); + self.tcx + .dcx() + .struct_span_err(*op_sp, "invalid `sym` operand") + .span_label_mv( + self.tcx.def_span(anon_const.def_id), + format!("is {} `{}`", ty.kind().article(), ty), + ) + .help_mv( + "`sym` operands must refer to either a function or a static", + ) + .emit(); } }; } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 4bac124b58bb..67ec2c3e5ea8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -197,11 +197,12 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() let mut res = Ok(()); if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) { let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span); - let mut err = - tcx.dcx().struct_span_err(sp, "impls of auto traits cannot be default"); - err.span_labels(impl_.defaultness_span, "default because of this"); - err.span_label(sp, "auto trait"); - res = Err(err.emit()); + res = Err(tcx + .dcx() + .struct_span_err(sp, "impls of auto traits cannot be default") + .span_labels_mv(impl_.defaultness_span, "default because of this") + .span_label_mv(sp, "auto trait") + .emit()); } // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span. match tcx.impl_polarity(def_id) { @@ -489,35 +490,33 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { if !unsatisfied_bounds.is_empty() { let plural = pluralize!(unsatisfied_bounds.len()); - let mut err = tcx.dcx().struct_span_err( - gat_item_hir.span, - format!("missing required bound{} on `{}`", plural, gat_item_hir.ident), - ); - let suggestion = format!( "{} {}", gat_item_hir.generics.add_where_or_trailing_comma(), unsatisfied_bounds.join(", "), ); - err.span_suggestion( - gat_item_hir.generics.tail_span_for_predicate_suggestion(), - format!("add the required where clause{plural}"), - suggestion, - Applicability::MachineApplicable, - ); - let bound = if unsatisfied_bounds.len() > 1 { "these bounds are" } else { "this bound is" }; - err.note(format!( - "{bound} currently required to ensure that impls have maximum flexibility" - )); - err.note( - "we are soliciting feedback, see issue #87479 \ + tcx.dcx() + .struct_span_err( + gat_item_hir.span, + format!("missing required bound{} on `{}`", plural, gat_item_hir.ident), + ) + .span_suggestion_mv( + gat_item_hir.generics.tail_span_for_predicate_suggestion(), + format!("add the required where clause{plural}"), + suggestion, + Applicability::MachineApplicable, + ) + .note_mv(format!( + "{bound} currently required to ensure that impls have maximum flexibility" + )) + .note_mv( + "we are soliciting feedback, see issue #87479 \ \ for more information", - ); - - err.emit(); + ) + .emit(); } } } @@ -938,8 +937,8 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), }; if may_suggest_feature && tcx.sess.is_nightly_build() { diag.help( - "add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types", - ); + "add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types", + ); } Err(diag.emit()) 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 ec15aa65e7ac..8f54bf00528c 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -70,17 +70,16 @@ impl<'tcx> InherentOverlapChecker<'tcx> { match seen_items.entry(norm_ident) { Entry::Occupied(entry) => { let former = entry.get(); - let mut err = struct_span_err!( + struct_span_err!( self.tcx.dcx(), span, E0592, "duplicate definitions with name `{}`", ident, - ); - err.span_label(span, format!("duplicate definitions for `{ident}`")); - err.span_label(*former, format!("other definition for `{ident}`")); - - err.emit(); + ) + .span_label_mv(span, format!("duplicate definitions for `{ident}`")) + .span_label_mv(*former, format!("other definition for `{ident}`")) + .emit(); } Entry::Vacant(entry) => { entry.insert(span); 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 c033cec31550..61bb4235139d 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -750,12 +750,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { kind: hir::ItemKind::OpaqueTy { .. }, .. }) = self.tcx.hir_node(parent_id) { - let mut err = self.tcx.dcx().struct_span_err( + self.tcx.dcx().struct_span_err( lifetime.ident.span, "higher kinded lifetime bounds on nested opaque types are not supported yet", - ); - err.span_note(self.tcx.def_span(def_id), "lifetime declared here"); - err.emit(); + ) + .span_note_mv(self.tcx.def_span(def_id), "lifetime declared here") + .emit(); self.uninsert_lifetime_on_error(lifetime, def.unwrap()); } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index da9d41a7d72d..c71639135173 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3183,9 +3183,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized); if !is_input && !expr.is_syntactic_place_expr() { - let mut err = self.dcx().struct_span_err(expr.span, "invalid asm output"); - err.span_label(expr.span, "cannot assign to this expression"); - err.emit(); + self.dcx() + .struct_span_err(expr.span, "invalid asm output") + .span_label_mv(expr.span, "cannot assign to this expression") + .emit(); } // If this is an input value, we require its type to be fully resolved @@ -3278,27 +3279,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter_enumerated() .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident) else { - let mut err = type_error_struct!( + type_error_struct!( self.dcx(), ident.span, container, E0599, "no variant named `{ident}` found for enum `{container}`", - ); - err.span_label(field.span, "variant not found"); - err.emit(); + ) + .span_label_mv(field.span, "variant not found") + .emit(); break; }; let Some(&subfield) = fields.next() else { - let mut err = type_error_struct!( + type_error_struct!( self.dcx(), ident.span, container, E0795, "`{ident}` is an enum variant; expected field at end of `offset_of`", - ); - err.span_label(field.span, "enum variant"); - err.emit(); + ) + .span_label_mv(field.span, "enum variant") + .emit(); break; }; let (subident, sub_def_scope) = @@ -3309,16 +3310,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter_enumerated() .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident) else { - let mut err = type_error_struct!( + type_error_struct!( self.dcx(), ident.span, container, E0609, "no field named `{subfield}` on enum variant `{container}::{ident}`", - ); - err.span_label(field.span, "this enum variant..."); - err.span_label(subident.span, "...does not have this field"); - err.emit(); + ) + .span_label_mv(field.span, "this enum variant...") + .span_label_mv(subident.span, "...does not have this field") + .emit(); break; }; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 022213abd670..4ac85cce292f 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1541,19 +1541,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let sp_comma = sm.end_point(pat.span.with_hi(sp_brace.hi())); let sugg = if no_fields || sp_brace != sp_comma { ".. }" } else { ", .. }" }; - let mut err = struct_span_err!( + struct_span_err!( self.dcx(), pat.span, E0638, "`..` required with {descr} marked as non-exhaustive", - ); - err.span_suggestion_verbose( + ) + .span_suggestion_verbose_mv( sp_comma, "add `..` at the end of the field list to ignore all other fields", sugg, Applicability::MachineApplicable, - ); - err.emit(); + ) + .emit(); } fn error_field_already_bound( diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index c3bcdcfa5cd8..3825c513ef3f 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -332,15 +332,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if inside_union && source.ty_adt_def().is_some_and(|adt| adt.is_manually_drop()) { - let mut err = self.dcx().struct_span_err( + self.dcx().struct_span_err( expr.span, "not automatically applying `DerefMut` on `ManuallyDrop` union field", - ); - err.help( + ) + .help_mv( "writing to this reference calls the destructor for the old value", - ); - err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor"); - err.emit(); + ) + .help_mv("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor") + .emit(); } } source = adjustment.target; diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index d45ec8e46469..212e831e1061 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -357,7 +357,6 @@ pub fn struct_lint_level( if let Level::Expect(_) = level { let name = lint.name_lower(); err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn: false }); - decorate(&mut err); err.emit(); return; diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index b4e45ad56856..b3c05a36a138 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -190,7 +190,7 @@ pub fn recursive_type_error( } s }; - let mut err = struct_span_err!( + struct_span_err!( tcx.dcx(), err_span, E0072, @@ -198,13 +198,13 @@ pub fn recursive_type_error( pluralize!(cycle_len), items_list, pluralize!("has", cycle_len), - ); - err.multipart_suggestion( + ) + .multipart_suggestion_mv( "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle", suggestion, Applicability::HasPlaceholders, - ); - err.emit(); + ) + .emit(); } fn find_item_ty_spans( diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 02eab2bac58b..66d80fa3c523 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2848,15 +2848,16 @@ impl<'a> Parser<'a> { let label = self.eat_label().expect("just checked if a label exists"); self.bump(); // eat `:` let span = label.ident.span.to(self.prev_token.span); - let mut err = self.dcx().struct_span_err(span, "block label not supported here"); - err.span_label(span, "not supported here"); - err.tool_only_span_suggestion( - label.ident.span.until(self.token.span), - "remove this block label", - "", - Applicability::MachineApplicable, - ); - err.emit(); + self.dcx() + .struct_span_err(span, "block label not supported here") + .span_label_mv(span, "not supported here") + .tool_only_span_suggestion_mv( + label.ident.span.until(self.token.span), + "remove this block label", + "", + Applicability::MachineApplicable, + ) + .emit(); true } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 2a8e220181a4..7ff72eb5fb3f 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -141,17 +141,18 @@ impl<'a> Parser<'a> { // Parse optional const generics default value. let default = if self.eat(&token::Eq) { Some(self.parse_const_arg()?) } else { None }; - let mut err = self.dcx().struct_span_err( - mistyped_const_ident.span, - format!("`const` keyword was mistyped as `{}`", mistyped_const_ident.as_str()), - ); - err.span_suggestion_verbose( - mistyped_const_ident.span, - "use the `const` keyword", - kw::Const.as_str(), - Applicability::MachineApplicable, - ); - err.emit(); + self.dcx() + .struct_span_err( + mistyped_const_ident.span, + format!("`const` keyword was mistyped as `{}`", mistyped_const_ident.as_str()), + ) + .span_suggestion_verbose_mv( + mistyped_const_ident.span, + "use the `const` keyword", + kw::Const.as_str(), + Applicability::MachineApplicable, + ) + .emit(); Ok(GenericParam { ident, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 2694dd3c9c61..381ea21a2446 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1109,8 +1109,7 @@ impl<'a> Parser<'a> { && self.token.is_keyword(kw::Unsafe) && self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Brace)) { - let err = self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err(); - err.emit(); + self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err().emit(); unsafety = Unsafe::Yes(self.token.span); self.eat_keyword(kw::Unsafe); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 43608da8ab2a..70519af16c58 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3282,16 +3282,16 @@ fn mk_where_bound_predicate( /// Report lifetime/lifetime shadowing as an error. pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) { - let mut err = struct_span_err!( + struct_span_err!( sess.dcx(), shadower.span, E0496, "lifetime name `{}` shadows a lifetime name that is already in scope", orig.name, - ); - err.span_label(orig.span, "first declared here"); - err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name)); - err.emit(); + ) + .span_label_mv(orig.span, "first declared here") + .span_label_mv(shadower.span, format!("lifetime `{}` already in scope", orig.name)) + .emit(); } struct LifetimeFinder<'ast> { @@ -3317,11 +3317,12 @@ impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> { pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) { let name = shadower.name; let shadower = shadower.span; - let mut err = sess.dcx().struct_span_warn( - shadower, - format!("label name `{name}` shadows a label name that is already in scope"), - ); - err.span_label(orig, "first declared here"); - err.span_label(shadower, format!("label `{name}` already in scope")); - err.emit(); + sess.dcx() + .struct_span_warn( + shadower, + format!("label name `{name}` shadows a label name that is already in scope"), + ) + .span_label_mv(orig, "first declared here") + .span_label_mv(shadower, format!("label `{name}` already in scope")) + .emit(); } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 32aed6611cfc..fc55481cb015 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -576,10 +576,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { err.add_as_non_derive = Some(AddAsNonDerive { macro_path: &path_str }); } - let mut err = self.dcx().create_err(err); - err.span_label(path.span, format!("not {article} {expected}")); - - err.emit(); + self.dcx() + .create_err(err) + .span_label_mv(path.span, format!("not {article} {expected}")) + .emit(); return Ok((self.dummy_ext(kind), Res::Err)); } @@ -830,7 +830,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { expected, ident, }); - self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident, krate); err.emit(); } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 6bf1307b37f3..51119c3560e6 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -3544,17 +3544,16 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span: Span, ) -> Option> { if !self.tcx.features().generic_const_exprs { - let mut err = self - .dcx() - .struct_span_err(span, "constant expression depends on a generic parameter"); - // FIXME(const_generics): we should suggest to the user how they can resolve this - // issue. However, this is currently not actually possible - // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). - // - // Note that with `feature(generic_const_exprs)` this case should not - // be reachable. - err.note("this may fail depending on what value the parameter takes"); - err.emit(); + self.dcx() + .struct_span_err(span, "constant expression depends on a generic parameter") + // FIXME(const_generics): we should suggest to the user how they can resolve this + // issue. However, this is currently not actually possible + // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). + // + // Note that with `feature(generic_const_exprs)` this case should not + // be reachable. + .note_mv("this may fail depending on what value the parameter takes") + .emit(); return None; } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 359d5ec485e3..40ea4346f933 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -495,16 +495,16 @@ impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> { .intersperse("::") .collect::() ); - let mut err = rustc_errors::struct_span_err!( + rustc_errors::struct_span_err!( self.tcx.dcx(), path.span, E0433, "failed to resolve: {label}", - ); - err.span_label(path.span, label); - err.note("this error was originally ignored because you are running `rustdoc`"); - err.note("try running again with `rustc` or `cargo check` and you may get a more detailed error"); - err.emit(); + ) + .span_label_mv(path.span, label) + .note_mv("this error was originally ignored because you are running `rustdoc`") + .note_mv("try running again with `rustc` or `cargo check` and you may get a more detailed error") + .emit(); } // We could have an outer resolution that succeeded, // but with generic parameters that failed. From 6682f243dcb9babd67525bbf9798dca302f60588 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 3 Jan 2024 21:50:36 +1100 Subject: [PATCH 204/257] Remove all eight `DiagnosticBuilder::*_with_code` methods. These all have relatively low use, and can be perfectly emulated with a simpler construction method combined with `code` or `code_mv`. --- .../rustc_errors/src/diagnostic_builder.rs | 4 +- compiler/rustc_errors/src/lib.rs | 92 ------------------- .../rustc_hir_analysis/src/astconv/mod.rs | 3 +- .../src/check/compare_impl_item.rs | 4 +- .../wrong_number_of_generic_args.rs | 3 +- compiler/rustc_hir_typeck/src/expr.rs | 8 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 29 +++--- compiler/rustc_hir_typeck/src/lib.rs | 9 +- .../src/infer/error_reporting/mod.rs | 18 ++-- compiler/rustc_parse/src/lexer/mod.rs | 73 +++++++-------- compiler/rustc_parse/src/parser/attr.rs | 9 +- compiler/rustc_resolve/src/diagnostics.rs | 4 +- .../rustc_resolve/src/late/diagnostics.rs | 4 +- .../error_reporting/type_err_ctxt_ext.rs | 4 +- 14 files changed, 84 insertions(+), 180 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index b3f8a6c24407..dcbf60e4847e 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -538,11 +538,11 @@ impl Drop for DiagnosticBuilder<'_, G> { #[macro_export] macro_rules! struct_span_err { ($dcx:expr, $span:expr, $code:ident, $($message:tt)*) => ({ - $dcx.struct_span_err_with_code( + $dcx.struct_span_err( $span, format!($($message)*), - $crate::error_code!($code), ) + .code_mv($crate::error_code!($code)) }) } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a63c32566ca7..4710722c7715 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -732,19 +732,6 @@ impl DiagCtxt { self.struct_warn(msg).span_mv(span) } - /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. - /// Also include a code. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_warn_with_code( - &self, - span: impl Into, - msg: impl Into, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_, ()> { - self.struct_span_warn(span, msg).code_mv(code) - } - /// Construct a builder at the `Warning` level with the `msg`. /// /// Attempting to `.emit()` the builder will only emit if either: @@ -785,18 +772,6 @@ impl DiagCtxt { self.struct_err(msg).span_mv(span) } - /// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_err_with_code( - &self, - span: impl Into, - msg: impl Into, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_> { - self.struct_span_err(span, msg).code_mv(code) - } - /// Construct a builder at the `Error` level with the `msg`. // FIXME: This method should be removed (every error should have an associated error code). #[rustc_lint_diagnostics] @@ -805,28 +780,6 @@ impl DiagCtxt { DiagnosticBuilder::new(self, Error, msg) } - /// Construct a builder at the `Error` level with the `msg` and the `code`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_err_with_code( - &self, - msg: impl Into, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_> { - self.struct_err(msg).code_mv(code) - } - - /// Construct a builder at the `Warn` level with the `msg` and the `code`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_warn_with_code( - &self, - msg: impl Into, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_, ()> { - self.struct_warn(msg).code_mv(code) - } - /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`. #[rustc_lint_diagnostics] #[track_caller] @@ -838,18 +791,6 @@ impl DiagCtxt { self.struct_fatal(msg).span_mv(span) } - /// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_fatal_with_code( - &self, - span: impl Into, - msg: impl Into, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_, FatalAbort> { - self.struct_span_fatal(span, msg).code_mv(code) - } - /// Construct a builder at the `Fatal` level with the `msg`. #[rustc_lint_diagnostics] #[track_caller] @@ -897,17 +838,6 @@ impl DiagCtxt { self.struct_span_fatal(span, msg).emit() } - #[rustc_lint_diagnostics] - #[track_caller] - pub fn span_fatal_with_code( - &self, - span: impl Into, - msg: impl Into, - code: DiagnosticId, - ) -> ! { - self.struct_span_fatal_with_code(span, msg, code).emit() - } - #[rustc_lint_diagnostics] #[track_caller] pub fn span_err( @@ -918,34 +848,12 @@ impl DiagCtxt { self.struct_span_err(span, msg).emit() } - #[rustc_lint_diagnostics] - #[track_caller] - pub fn span_err_with_code( - &self, - span: impl Into, - msg: impl Into, - code: DiagnosticId, - ) -> ErrorGuaranteed { - self.struct_span_err_with_code(span, msg, code).emit() - } - #[rustc_lint_diagnostics] #[track_caller] pub fn span_warn(&self, span: impl Into, msg: impl Into) { self.struct_span_warn(span, msg).emit() } - #[rustc_lint_diagnostics] - #[track_caller] - pub fn span_warn_with_code( - &self, - span: impl Into, - msg: impl Into, - code: DiagnosticId, - ) { - self.struct_span_warn_with_code(span, msg, code).emit() - } - pub fn span_bug(&self, span: impl Into, msg: impl Into) -> ! { self.struct_span_bug(span, msg).emit() } diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 0a924aa3536e..eb6a5cc5b217 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1647,7 +1647,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let msg = format!("{kind} `{name}` is private"); let def_span = tcx.def_span(item); tcx.dcx() - .struct_span_err_with_code(span, msg, rustc_errors::error_code!(E0624)) + .struct_span_err(span, msg) + .code_mv(rustc_errors::error_code!(E0624)) .span_label_mv(span, format!("private {kind}")) .span_label_mv(def_span, format!("{kind} defined here")) .emit(); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index f5323fd3bb8d..46b5c1a94dec 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1371,7 +1371,7 @@ fn compare_number_of_generics<'tcx>( let spans = arg_spans(impl_.kind, impl_item.generics); let span = spans.first().copied(); - let mut err = tcx.dcx().struct_span_err_with_code( + let mut err = tcx.dcx().struct_span_err( spans, format!( "{} `{}` has {} {kind} parameter{} but its trait \ @@ -1384,8 +1384,8 @@ fn compare_number_of_generics<'tcx>( pluralize!(trait_count), kind = kind, ), - DiagnosticId::Error("E0049".into()), ); + err.code(DiagnosticId::Error("E0049".into())); let msg = format!("expected {trait_count} {kind} parameter{}", pluralize!(trait_count),); diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index ae68a8bf2811..04c42b4b2e63 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -523,8 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> { let span = self.path_segment.ident.span; let msg = self.create_error_message(); - - self.tcx.dcx().struct_span_err_with_code(span, msg, self.code()) + self.tcx.dcx().struct_span_err(span, msg).code_mv(self.code()) } /// Builds the `expected 1 type argument / supplied 2 type arguments` message. diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index c71639135173..7617f03fcf29 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -940,12 +940,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - // FIXME: Make this use Diagnostic once error codes can be dynamically set. - let mut err = self.dcx().struct_span_err_with_code( - op_span, - "invalid left-hand side of assignment", - DiagnosticId::Error(err_code.into()), - ); + let mut err = self.dcx().struct_span_err(op_span, "invalid left-hand side of assignment"); + err.code(DiagnosticId::Error(err_code.into())); err.span_label(lhs.span, "cannot assign to this expression"); self.comes_from_while_condition(lhs.hir_id, |expr| { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 9b8916911618..da6f2042c110 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -664,7 +664,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {call_name} are incorrect"), ); } else { - err = tcx.dcx().struct_span_err_with_code( + err = tcx.dcx().struct_span_err( full_call_span, format!( "{call_name} takes {}{} but {} {} supplied", @@ -676,8 +676,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { potentially_plural_count(provided_args.len(), "argument"), pluralize!("was", provided_args.len()) ), - DiagnosticId::Error(err_code.to_owned()), ); + err.code(DiagnosticId::Error(err_code.to_owned())); err.multipart_suggestion_verbose( "wrap these arguments in parentheses to construct a tuple", vec![ @@ -815,18 +815,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_name, ) } else { - tcx.dcx().struct_span_err_with_code( - full_call_span, - format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count(formal_and_expected_inputs.len(), "argument"), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - DiagnosticId::Error(err_code.to_owned()), - ) + tcx.dcx() + .struct_span_err( + full_call_span, + format!( + "this {} takes {}{} but {} {} supplied", + call_name, + if c_variadic { "at least " } else { "" }, + potentially_plural_count(formal_and_expected_inputs.len(), "argument"), + potentially_plural_count(provided_args.len(), "argument"), + pluralize!("was", provided_args.len()) + ), + ) + .code_mv(DiagnosticId::Error(err_code.to_owned())) }; // As we encounter issues, keep track of what we want to provide for the suggestion diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index c3332a493067..ffae08d0f27c 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -366,11 +366,10 @@ fn report_unexpected_variant_res( _ => res.descr(), }; let path_str = rustc_hir_pretty::qpath_to_string(qpath); - let err = tcx.dcx().struct_span_err_with_code( - span, - format!("expected {expected}, found {res_descr} `{path_str}`"), - DiagnosticId::Error(err_code.into()), - ); + let err = tcx + .dcx() + .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) + .code_mv(DiagnosticId::Error(err_code.into())); match res { Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == "E0164" => { let patterns_url = "https://doc.rust-lang.org/book/ch18-00-patterns.html"; diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index f88fd617c13e..5dd4f959bebf 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2356,15 +2356,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }, }; - let mut err = self.tcx.dcx().struct_span_err_with_code( - span, - format!("{labeled_user_string} may not live long enough"), - match sub.kind() { - ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => error_code!(E0309), - ty::ReStatic => error_code!(E0310), - _ => error_code!(E0311), - }, - ); + let mut err = self + .tcx + .dcx() + .struct_span_err(span, format!("{labeled_user_string} may not live long enough")); + err.code(match sub.kind() { + ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => error_code!(E0309), + ty::ReStatic => error_code!(E0310), + _ => error_code!(E0311), + }); '_explain: { let (description, span) = match sub.kind() { diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index bc42b1b3ef84..d08f31e69d88 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -395,51 +395,58 @@ impl<'a> StringReader<'a> { match kind { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { - self.dcx().span_fatal_with_code( - self.mk_sp(start, end), - "unterminated character literal", - error_code!(E0762), - ) + self.dcx() + .struct_span_fatal(self.mk_sp(start, end), "unterminated character literal") + .code_mv(error_code!(E0762)) + .emit() } self.cook_quoted(token::Char, Mode::Char, start, end, 1, 1) // ' ' } rustc_lexer::LiteralKind::Byte { terminated } => { if !terminated { - self.dcx().span_fatal_with_code( - self.mk_sp(start + BytePos(1), end), - "unterminated byte constant", - error_code!(E0763), - ) + self.dcx() + .struct_span_fatal( + self.mk_sp(start + BytePos(1), end), + "unterminated byte constant", + ) + .code_mv(error_code!(E0763)) + .emit() } self.cook_quoted(token::Byte, Mode::Byte, start, end, 2, 1) // b' ' } rustc_lexer::LiteralKind::Str { terminated } => { if !terminated { - self.dcx().span_fatal_with_code( - self.mk_sp(start, end), - "unterminated double quote string", - error_code!(E0765), - ) + self.dcx() + .struct_span_fatal( + self.mk_sp(start, end), + "unterminated double quote string", + ) + .code_mv(error_code!(E0765)) + .emit() } self.cook_quoted(token::Str, Mode::Str, start, end, 1, 1) // " " } rustc_lexer::LiteralKind::ByteStr { terminated } => { if !terminated { - self.dcx().span_fatal_with_code( - self.mk_sp(start + BytePos(1), end), - "unterminated double quote byte string", - error_code!(E0766), - ) + self.dcx() + .struct_span_fatal( + self.mk_sp(start + BytePos(1), end), + "unterminated double quote byte string", + ) + .code_mv(error_code!(E0766)) + .emit() } self.cook_quoted(token::ByteStr, Mode::ByteStr, start, end, 2, 1) // b" " } rustc_lexer::LiteralKind::CStr { terminated } => { if !terminated { - self.dcx().span_fatal_with_code( - self.mk_sp(start + BytePos(1), end), - "unterminated C string", - error_code!(E0767), - ) + self.dcx() + .struct_span_fatal( + self.mk_sp(start + BytePos(1), end), + "unterminated C string", + ) + .code_mv(error_code!(E0767)) + .emit() } self.cook_c_string(token::CStr, Mode::CStr, start, end, 2, 1) // c" " } @@ -573,12 +580,9 @@ impl<'a> StringReader<'a> { possible_offset: Option, found_terminators: u32, ) -> ! { - let mut err = self.dcx().struct_span_fatal_with_code( - self.mk_sp(start, start), - "unterminated raw string", - error_code!(E0748), - ); - + let mut err = + self.dcx().struct_span_fatal(self.mk_sp(start, start), "unterminated raw string"); + err.code(error_code!(E0748)); err.span_label(self.mk_sp(start, start), "unterminated raw string"); if n_hashes > 0 { @@ -609,11 +613,8 @@ impl<'a> StringReader<'a> { None => "unterminated block comment", }; let last_bpos = self.pos; - let mut err = self.dcx().struct_span_fatal_with_code( - self.mk_sp(start, last_bpos), - msg, - error_code!(E0758), - ); + let mut err = self.dcx().struct_span_fatal(self.mk_sp(start, last_bpos), msg); + err.code(error_code!(E0758)); let mut nested_block_comment_open_idxs = vec![]; let mut last_nested_block_comment_idxs = None; let mut content_chars = self.str_from(start).char_indices().peekable(); diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 1f8c368b2a99..cec657f7b67a 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -55,11 +55,10 @@ impl<'a> Parser<'a> { } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind { if attr_style != ast::AttrStyle::Outer { let span = self.token.span; - let mut err = self.dcx().struct_span_err_with_code( - span, - fluent::parse_inner_doc_comment_not_permitted, - error_code!(E0753), - ); + let mut err = self + .dcx() + .struct_span_err(span, fluent::parse_inner_doc_comment_not_permitted); + err.code(error_code!(E0753)); if let Some(replacement_span) = self.annotate_following_item_if_applicable( &mut err, span, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 977a7e796e78..57f23200e795 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -944,13 +944,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { trait_item_span, trait_path, } => { - self.dcx().struct_span_err_with_code( + self.dcx().struct_span_err( span, format!( "item `{name}` is an associated {kind}, which doesn't match its trait `{trait_path}`", ), - code, ) + .code_mv(code) .span_label_mv(span, "does not match trait") .span_label_mv(trait_item_span, "item in trait") } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 70519af16c58..a36d1377ab5f 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -429,8 +429,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let base_error = self.make_base_error(path, span, source, res); let code = source.error_code(res.is_some()); - let mut err = - self.r.dcx().struct_span_err_with_code(base_error.span, base_error.msg.clone(), code); + let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone()); + err.code(code); self.suggest_at_operator_in_slice_pat_with_range(&mut err, path); self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 51119c3560e6..b05895e4b9a3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -2525,14 +2525,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Replace the more general E0283 with a more specific error err.cancel(); - err = self.dcx().struct_span_err_with_code( + err = self.dcx().struct_span_err( span, format!( "cannot {verb} associated {noun} on trait without specifying the \ corresponding `impl` type", ), - rustc_errors::error_code!(E0790), ); + err.code(rustc_errors::error_code!(E0790)); if let Some(local_def_id) = data.trait_ref.def_id.as_local() && let Some(hir::Node::Item(hir::Item { From 1881055000485527e3ad69e2bc6c9fa247e3f06f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 5 Jan 2024 09:55:07 +1100 Subject: [PATCH 205/257] Remove a `DiagnosticBuilder::emit_without_consuming` call. In this parsing recovery function, we only need to emit the previously obtained error message and mark `expr` as erroneous in the case where we actually recover. --- compiler/rustc_parse/src/parser/diagnostics.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 66d80fa3c523..b05940f3569a 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1212,29 +1212,32 @@ impl<'a> Parser<'a> { match x { Ok((_, _, false)) => { if self.eat(&token::Gt) { + // We made sense of it. Improve the error message. e.span_suggestion_verbose( binop.span.shrink_to_lo(), fluent::parse_sugg_turbofish_syntax, "::", Applicability::MaybeIncorrect, - ) - .emit_without_consuming(); + ); match self.parse_expr() { Ok(_) => { + // The subsequent expression is valid. Mark + // `expr` as erroneous and emit `e` now, but + // return `Ok` so parsing can continue. + e.emit(); *expr = self.mk_expr_err(expr.span.to(self.prev_token.span)); return Ok(()); } Err(err) => { - *expr = self.mk_expr_err(expr.span); err.cancel(); } } } } + Ok((_, _, true)) => {} Err(err) => { err.cancel(); } - _ => {} } } Err(e) From 3ce34f42e160d7e63132c7e3994861bb876eb943 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 5 Jan 2024 10:42:31 +1100 Subject: [PATCH 206/257] Remove a second `DiagnosticBuilder::emit_without_consuming` call. Instead of taking `seq` as a mutable reference, `maybe_recover_struct_lit_bad_delims` now consumes `seq` on the recovery path, and returns `seq` unchanged on the non-recovery path. The commit also combines an `if` and a `match` to merge two identical paths. Also change `recover_seq_parse_error` so it receives a `PErr` instead of a `PResult`, because all the call sites now handle the `Ok`/`Err` distinction themselves. --- .../rustc_parse/src/parser/diagnostics.rs | 17 +++--- compiler/rustc_parse/src/parser/expr.rs | 52 +++++++++---------- 2 files changed, 30 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index b05940f3569a..776d0ace8754 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -35,7 +35,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, FatalError, - PResult, + PErr, PResult, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -2044,17 +2044,12 @@ impl<'a> Parser<'a> { &mut self, delim: Delimiter, lo: Span, - result: PResult<'a, P>, + err: PErr<'a>, ) -> P { - match result { - Ok(x) => x, - Err(err) => { - err.emit(); - // Recover from parse error, callers expect the closing delim to be consumed. - self.consume_block(delim, ConsumeClosingDelim::Yes); - self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err) - } - } + err.emit(); + // Recover from parse error, callers expect the closing delim to be consumed. + self.consume_block(delim, ConsumeClosingDelim::Yes); + self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err) } /// Eats tokens until we can be relatively sure we reached the end of the diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 697c44a9f3e2..db777266b59d 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1272,15 +1272,13 @@ impl<'a> Parser<'a> { }; let open_paren = self.token.span; - let mut seq = self + let seq = self .parse_expr_paren_seq() .map(|args| self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))); - if let Some(expr) = - self.maybe_recover_struct_lit_bad_delims(lo, open_paren, &mut seq, snapshot) - { - return expr; + match self.maybe_recover_struct_lit_bad_delims(lo, open_paren, seq, snapshot) { + Ok(expr) => expr, + Err(err) => self.recover_seq_parse_error(Delimiter::Parenthesis, lo, err), } - self.recover_seq_parse_error(Delimiter::Parenthesis, lo, seq) } /// If we encounter a parser state that looks like the user has written a `struct` literal with @@ -1290,14 +1288,11 @@ impl<'a> Parser<'a> { &mut self, lo: Span, open_paren: Span, - seq: &mut PResult<'a, P>, + seq: PResult<'a, P>, snapshot: Option<(SnapshotParser<'a>, ExprKind)>, - ) -> Option> { - if !self.may_recover() { - return None; - } - match (seq.as_mut(), snapshot) { - (Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => { + ) -> PResult<'a, P> { + match (self.may_recover(), seq, snapshot) { + (true, Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => { snapshot.bump(); // `(` match snapshot.parse_struct_fields(path.clone(), false, Delimiter::Parenthesis) { Ok((fields, ..)) @@ -1315,11 +1310,13 @@ impl<'a> Parser<'a> { if !fields.is_empty() && // `token.kind` should not be compared here. // This is because the `snapshot.token.kind` is treated as the same as - // that of the open delim in `TokenTreesReader::parse_token_tree`, even if they are different. + // that of the open delim in `TokenTreesReader::parse_token_tree`, even + // if they are different. self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")") { - let mut replacement_err = - self.dcx().create_err(errors::ParenthesesWithStructFields { + err.cancel(); + self.dcx() + .create_err(errors::ParenthesesWithStructFields { span, r#type: path, braces_for_struct: errors::BracesForStructLiteral { @@ -1332,23 +1329,22 @@ impl<'a> Parser<'a> { .map(|field| field.span.until(field.expr.span)) .collect(), }, - }); - replacement_err.emit_without_consuming(); - - let old_err = mem::replace(err, replacement_err); - old_err.cancel(); + }) + .emit(); } else { - err.emit_without_consuming(); + err.emit(); } - return Some(self.mk_expr_err(span)); + Ok(self.mk_expr_err(span)) + } + Ok(_) => Err(err), + Err(err2) => { + err2.cancel(); + Err(err) } - Ok(_) => {} - Err(err) => err.cancel(), } } - _ => {} + (_, seq, _) => seq, } - None } /// Parse an indexing expression `expr[...]`. @@ -1552,7 +1548,7 @@ impl<'a> Parser<'a> { ) { Ok(x) => x, Err(err) => { - return Ok(self.recover_seq_parse_error(Delimiter::Parenthesis, lo, Err(err))); + return Ok(self.recover_seq_parse_error(Delimiter::Parenthesis, lo, err)); } }; let kind = if es.len() == 1 && !trailing_comma { From 1b6c8e7533ae387ed320fc55a45dee95acc311ee Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 5 Jan 2024 10:55:45 +1100 Subject: [PATCH 207/257] Remove a third `DiagnosticBuilder::emit_without_consuming` call. It's not clear why this was here, because the created error is returned as a normal error anyway. Nor is it clear why removing the call works. The change doesn't affect any tests; `tests/ui/parser/issues/issue-102182-impl-trait-recover.rs` looks like the only test that could have been affected. --- compiler/rustc_parse/src/parser/generics.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 7ff72eb5fb3f..66aa8cbcda4a 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -77,7 +77,6 @@ impl<'a> Parser<'a> { Applicability::MachineApplicable, ); } - err.emit_without_consuming(); return Err(err); } } From c733a0216d55977e51a26ead6f2446668f006e02 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 5 Jan 2024 11:10:18 +1100 Subject: [PATCH 208/257] Remove a fourth `DiagnosticBuilder::emit_without_consuming` call. The old code was very hard to understand, involving an `emit_without_consuming` call *and* a `delay_as_bug_without_consuming` call. With slight changes both calls can be avoided. Not creating the error until later is crucial, as is the early return in the `if recovered` block. It took me some time to come up with this reworking -- it went through intermediate states much further from the original code than this final version -- and it's isn't obvious at a glance that it is equivalent. But I think it is, and the unchanged test behaviour is good supporting evidence. The commit also changes `check_trailing_angle_brackets` to return `Option`. This provides a stricter proof that it emitted an error message than asserting `dcx.has_errors().is_some()`, which would succeed if any error had previously been emitted anywhere. --- .../rustc_parse/src/parser/diagnostics.rs | 17 +++++---- compiler/rustc_parse/src/parser/item.rs | 35 +++++++------------ 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 776d0ace8754..a9cf26d991c7 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -34,8 +34,8 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, FatalError, - PErr, PResult, + pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, + ErrorGuaranteed, FatalError, PErr, PResult, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -1049,9 +1049,9 @@ impl<'a> Parser<'a> { &mut self, segment: &PathSegment, end: &[&TokenKind], - ) -> bool { + ) -> Option { if !self.may_recover() { - return false; + return None; } // This function is intended to be invoked after parsing a path segment where there are two @@ -1086,7 +1086,7 @@ impl<'a> Parser<'a> { parsed_angle_bracket_args, ); if !parsed_angle_bracket_args { - return false; + return None; } // Keep the span at the start so we can highlight the sequence of `>` characters to be @@ -1124,7 +1124,7 @@ impl<'a> Parser<'a> { number_of_gt, number_of_shr, ); if number_of_gt < 1 && number_of_shr < 1 { - return false; + return None; } // Finally, double check that we have our end token as otherwise this is the @@ -1139,10 +1139,9 @@ impl<'a> Parser<'a> { let span = lo.until(self.token.span); let num_extra_brackets = number_of_gt + number_of_shr * 2; - self.dcx().emit_err(UnmatchedAngleBrackets { span, num_extra_brackets }); - return true; + return Some(self.dcx().emit_err(UnmatchedAngleBrackets { span, num_extra_brackets })); } - false + None } /// Check if a method call with an intended turbofish has been written without surrounding diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 381ea21a2446..fed3c83a81d1 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1793,52 +1793,43 @@ impl<'a> Parser<'a> { } _ => { let sp = self.prev_token.span.shrink_to_hi(); - let mut err = self.dcx().struct_span_err( - sp, - format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)), - ); + let msg = + format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)); // Try to recover extra trailing angle brackets - let mut recovered = false; if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind { if let Some(last_segment) = segments.last() { - recovered = self.check_trailing_angle_brackets( + let guar = self.check_trailing_angle_brackets( last_segment, &[&token::Comma, &token::CloseDelim(Delimiter::Brace)], ); - if recovered { + if let Some(_guar) = guar { // Handle a case like `Vec>,` where we can continue parsing fields // after the comma self.eat(&token::Comma); - // `check_trailing_angle_brackets` already emitted a nicer error - // NOTE(eddyb) this was `.cancel()`, but `err` - // gets returned, so we can't fully defuse it. - err.delay_as_bug_without_consuming(); + + // `check_trailing_angle_brackets` already emitted a nicer error, as + // proven by the presence of `_guar`. We can continue parsing. + return Ok(a_var); } } } + let mut err = self.dcx().struct_span_err(sp, msg); + if self.token.is_ident() || (self.token.kind == TokenKind::Pound && (self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Bracket)))) { - // This is likely another field, TokenKind::Pound is used for `#[..]` attribute for next field, - // emit the diagnostic and keep going + // This is likely another field, TokenKind::Pound is used for `#[..]` + // attribute for next field. Emit the diagnostic and continue parsing. err.span_suggestion( sp, "try adding a comma", ",", Applicability::MachineApplicable, ); - err.emit_without_consuming(); - recovered = true; - } - - if recovered { - // Make sure an error was emitted (either by recovering an angle bracket, - // or by finding an identifier as the next token), since we're - // going to continue parsing - assert!(self.dcx().has_errors().is_some()); + err.emit(); } else { return Err(err); } From d406278180f1cd7792f50be890658f2739c621fb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 5 Jan 2024 15:25:14 +1100 Subject: [PATCH 209/257] Remove `DiagnosticBuilder::emit_without_consuming`. A nice cleanup: it's now impossible to directly emit a `DiagnosticBuilder` without consuming it. --- compiler/rustc_errors/src/diagnostic_builder.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index dcbf60e4847e..eb081df040a9 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -269,12 +269,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { G::emit_producing_guarantee(&mut self) } - /// Emit the diagnostic without consuming it. `emit` should be preferred. - #[track_caller] - pub fn emit_without_consuming(&mut self) -> G::EmitResult { - G::emit_producing_guarantee(self) - } - /// Emit the diagnostic unless `delay` is true, /// in which case the emission will be delayed as a bug. /// @@ -376,7 +370,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { #[track_caller] pub fn delay_as_bug_without_consuming(&mut self) -> G::EmitResult { self.downgrade_to_delayed_bug(); - self.emit_without_consuming() + G::emit_producing_guarantee(self) } forward!((span_label, span_label_mv)( From 4752a923af2a0e26b2da0861e4a8f1f6c35b6d56 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 5 Jan 2024 16:38:04 +1100 Subject: [PATCH 210/257] Remove `DiagnosticBuilder::delay_as_bug_without_consuming`. The existing uses are replaced in one of three ways. - In a function that also has calls to `emit`, just rearrange the code so that exactly one of `delay_as_bug` or `emit` is called on every path. - In a function returning a `DiagnosticBuilder`, use `downgrade_to_delayed_bug`. That's good enough because it will get emitted later anyway. - In `unclosed_delim_err`, one set of errors is being replaced with another set, so just cancel the original errors. --- compiler/rustc_errors/src/diagnostic_builder.rs | 7 ------- compiler/rustc_hir_typeck/src/intrinsicck.rs | 8 +++++--- compiler/rustc_hir_typeck/src/op.rs | 2 +- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 2 +- compiler/rustc_infer/src/infer/error_reporting/note.rs | 2 +- compiler/rustc_parse/src/lexer/mod.rs | 2 +- compiler/rustc_parse/src/lexer/tokentrees.rs | 6 +++--- compiler/rustc_parse/src/parser/pat.rs | 7 ++++--- compiler/rustc_passes/src/errors.rs | 2 +- .../src/traits/error_reporting/suggestions.rs | 2 +- 10 files changed, 18 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index eb081df040a9..6fd485e00add 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -366,13 +366,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { self.emit() } - /// Non-consuming variant of `delay_as_bug`. - #[track_caller] - pub fn delay_as_bug_without_consuming(&mut self) -> G::EmitResult { - self.downgrade_to_delayed_bug(); - G::emit_producing_guarantee(self) - } - forward!((span_label, span_label_mv)( span: Span, label: impl Into, diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index a18f486b0d25..8bffd2dfc70a 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -121,15 +121,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); if from == to { err.note(format!("`{from}` does not have a fixed size")); + err.emit(); } else { err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) .note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); if let Err(LayoutError::ReferencesError(_)) = sk_from { - err.delay_as_bug_without_consuming(); + err.delay_as_bug(); } else if let Err(LayoutError::ReferencesError(_)) = sk_to { - err.delay_as_bug_without_consuming(); + err.delay_as_bug(); + } else { + err.emit(); } } - err.emit(); } } diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 59cf0d8c8ed6..3b5226c64143 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -385,7 +385,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::ExprKind::Assign(..) = expr.kind { // We defer to the later error produced by `check_lhs_assignable`. - err.delay_as_bug_without_consuming(); + err.downgrade_to_delayed_bug(); } let suggest_deref_binop = diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 5dd4f959bebf..b1c360b61cb6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -361,7 +361,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( ); } ty::ReError(_) => { - err.delay_as_bug_without_consuming(); + err.downgrade_to_delayed_bug(); } _ => { // Ugh. This is a painful case: the hidden region is not one diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index c09646b491c0..77a0accf80bb 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -281,7 +281,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } }; if sub.is_error() || sup.is_error() { - err.delay_as_bug_without_consuming(); + err.downgrade_to_delayed_bug(); } err } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index d08f31e69d88..a45bc5812402 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -67,7 +67,7 @@ pub(crate) fn parse_token_trees<'a>( let (stream, res, unmatched_delims) = tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); match res { - Ok(_open_spacing) if unmatched_delims.is_empty() => Ok(stream), + Ok(()) if unmatched_delims.is_empty() => Ok(stream), _ => { // Return error if there are unmatched delimiters or unclosed delimiters. // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 72f76cd89756..64b3928689af 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -277,9 +277,9 @@ impl<'a> TokenTreesReader<'a> { parser.bump(); } if !diff_errs.is_empty() { - errs.iter_mut().for_each(|err| { - err.delay_as_bug_without_consuming(); - }); + for err in errs { + err.cancel(); + } return diff_errs; } return errs; diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index a6804b992043..00dc307ab60a 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -242,7 +242,7 @@ impl<'a> Parser<'a> { Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, pat }) }; - let mut err = self.dcx().create_err(match syntax_loc { + let err = self.dcx().create_err(match syntax_loc { PatternLocation::LetBinding => { TopLevelOrPatternNotAllowed::LetBinding { span, sub } } @@ -251,9 +251,10 @@ impl<'a> Parser<'a> { } }); if trailing_vert { - err.delay_as_bug_without_consuming(); + err.delay_as_bug(); + } else { + err.emit(); } - err.emit(); } Ok((pat, colon)) diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 44c71dfa8d02..d934e959a417 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1040,7 +1040,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'_, G> for BreakNonLoop<'a> { // This error is redundant, we will have already emitted a // suggestion to use the label when `segment` wasn't found // (hence the `Res::Err` check). - diag.delay_as_bug_without_consuming(); + diag.downgrade_to_delayed_bug(); } _ => { diag.span_suggestion( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index fb1df4e4e766..4a6ef27e7ba3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2088,7 +2088,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let ty = self.resolve_vars_if_possible(returned_ty); if ty.references_error() { // don't print out the [type error] here - err.delay_as_bug_without_consuming(); + err.downgrade_to_delayed_bug(); } else { err.span_label(expr.span, format!("this returned value is of type `{ty}`")); } From 0cb486bc5b18fcce37f616bb46b054ebea1028cd Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 5 Jan 2024 17:33:12 +1100 Subject: [PATCH 211/257] Make `emit_producing_{guarantee,nothing}` consuming. This is now possible, thanks to changes in previous commits. --- .../rustc_errors/src/diagnostic_builder.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 6fd485e00add..b81e37501e7e 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -95,12 +95,12 @@ pub trait EmissionGuarantee: Sized { /// `impl` of `EmissionGuarantee`, to make it impossible to create a value /// of `Self::EmitResult` without actually performing the emission. #[track_caller] - fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult; + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult; } impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// Most `emit_producing_guarantee` functions use this as a starting point. - fn emit_producing_nothing(&mut self) { + fn emit_producing_nothing(mut self) { match self.state { // First `.emit()` call, the `&DiagCtxt` is still available. DiagnosticBuilderState::Emittable(dcx) => { @@ -115,7 +115,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`. impl EmissionGuarantee for ErrorGuaranteed { - fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + fn emit_producing_guarantee(mut db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { // Contrast this with `emit_producing_nothing`. match db.state { // First `.emit()` call, the `&DiagCtxt` is still available. @@ -156,7 +156,7 @@ impl EmissionGuarantee for ErrorGuaranteed { // FIXME(eddyb) should there be a `Option` impl as well? impl EmissionGuarantee for () { - fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { db.emit_producing_nothing(); } } @@ -169,7 +169,7 @@ pub struct BugAbort; impl EmissionGuarantee for BugAbort { type EmitResult = !; - fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { db.emit_producing_nothing(); panic::panic_any(ExplicitBug); } @@ -183,14 +183,14 @@ pub struct FatalAbort; impl EmissionGuarantee for FatalAbort { type EmitResult = !; - fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { db.emit_producing_nothing(); crate::FatalError.raise() } } impl EmissionGuarantee for rustc_span::fatal_error::FatalError { - fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { db.emit_producing_nothing(); rustc_span::fatal_error::FatalError } @@ -265,8 +265,8 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// Emit and consume the diagnostic. #[track_caller] - pub fn emit(mut self) -> G::EmitResult { - G::emit_producing_guarantee(&mut self) + pub fn emit(self) -> G::EmitResult { + G::emit_producing_guarantee(self) } /// Emit the diagnostic unless `delay` is true, From 2d91c6d1bf900276de71a7bde89ffe9cffed59fa Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 8 Jan 2024 07:47:02 +1100 Subject: [PATCH 212/257] Remove `DiagnosticBuilderState`. Currently it's used for two dynamic checks: - When a diagnostic is emitted, has it been emitted before? - When a diagnostic is dropped, has it been emitted/cancelled? The first check is no longer need, because `emit` is consuming, so it's impossible to emit a `DiagnosticBuilder` twice. The second check is still needed. This commit replaces `DiagnosticBuilderState` with a simpler `Option>`, which is enough for the second check: functions like `emit` and `cancel` can take the `Diagnostic` and then `drop` can check that the `Diagnostic` was taken. The `DiagCtxt` reference from `DiagnosticBuilderState` is now stored as its own field, removing the need for the `dcx` method. As well as making the code shorter and simpler, the commit removes: - One (deprecated) `ErrorGuaranteed::unchecked_claim_error_was_emitted` call. - Two `FIXME(eddyb)` comments that are no longer relevant. - The use of a dummy `Diagnostic` in `into_diagnostic`. Nice! --- .../rustc_errors/src/diagnostic_builder.rs | 213 ++++++------------ compiler/rustc_errors/src/lib.rs | 1 + compiler/rustc_mir_transform/src/errors.rs | 3 +- 3 files changed, 74 insertions(+), 143 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index b81e37501e7e..e72791d89a29 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -35,19 +35,28 @@ where } /// Used for emitting structured error messages and other diagnostic information. +/// Each constructed `DiagnosticBuilder` must be consumed by a function such as +/// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a +/// `DiagnosticBuilder` is dropped without being consumed by one of these +/// functions. /// /// If there is some state in a downstream crate you would like to /// access in the methods of `DiagnosticBuilder` here, consider /// extending `DiagCtxtFlags`. #[must_use] pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { - state: DiagnosticBuilderState<'a>, + pub dcx: &'a DiagCtxt, - /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a - /// return value, especially within the frequently-used `PResult` type. - /// In theory, return value optimization (RVO) should avoid unnecessary - /// copying. In practice, it does not (at the time of writing). - diagnostic: Box, + /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is + /// consumed via `emit`, `cancel`, etc. At that point it is consumed and + /// replaced with `None`. Then `drop` checks that it is `None`; if not, it + /// panics because a diagnostic was built but not used. + /// + /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is + /// often used as a return value, especially within the frequently-used + /// `PResult` type. In theory, return value optimization (RVO) should avoid + /// unnecessary copying. In practice, it does not (at the time of writing). + diag: Option>, _marker: PhantomData, } @@ -56,32 +65,9 @@ pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { // twice, which would be bad. impl !Clone for DiagnosticBuilder<'_, G> {} -#[derive(Clone)] -enum DiagnosticBuilderState<'a> { - /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`. - /// - /// The `Diagnostic` will be emitted through this `DiagCtxt`. - Emittable(&'a DiagCtxt), - - /// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`. - /// - /// The `Diagnostic` will be ignored when calling `.emit()`, and it can be - /// assumed that `.emit()` was previously called, to end up in this state. - /// - /// While this is also used by `.cancel()`, this state is only observed by - /// the `Drop` `impl` of `DiagnosticBuilder`, because `.cancel()` takes - /// `self` by-value specifically to prevent any attempts to `.emit()`. - /// - // FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`, - // despite that being potentially lossy, if important information is added - // *after* the original `.emit()` call. - AlreadyEmittedOrDuringCancellation, -} - -// `DiagnosticBuilderState` should be pointer-sized. rustc_data_structures::static_assert_size!( - DiagnosticBuilderState<'_>, - std::mem::size_of::<&DiagCtxt>() + DiagnosticBuilder<'_, ()>, + 2 * std::mem::size_of::() ); /// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee" @@ -99,62 +85,44 @@ pub trait EmissionGuarantee: Sized { } impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { + /// Takes the diagnostic. For use by methods that consume the + /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the + /// only code that will be run on `self`. + fn take_diag(&mut self) -> Diagnostic { + Box::into_inner(self.diag.take().unwrap()) + } + /// Most `emit_producing_guarantee` functions use this as a starting point. fn emit_producing_nothing(mut self) { - match self.state { - // First `.emit()` call, the `&DiagCtxt` is still available. - DiagnosticBuilderState::Emittable(dcx) => { - self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; - dcx.emit_diagnostic_without_consuming(&mut self.diagnostic); - } - // `.emit()` was previously called, disallowed from repeating it. - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} - } + let diag = self.take_diag(); + self.dcx.emit_diagnostic(diag); + } + + /// `ErrorGuaranteed::emit_producing_guarantee` uses this. + // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`. + fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed { + let diag = self.take_diag(); + + // Only allow a guarantee if the `level` wasn't switched to a + // non-error. The field isn't `pub`, but the whole `Diagnostic` can be + // overwritten with a new one, thanks to `DerefMut`. + assert!( + diag.is_error(), + "emitted non-error ({:?}) diagnostic from `DiagnosticBuilder`", + diag.level, + ); + + let guar = self.dcx.emit_diagnostic(diag); + guar.unwrap() } } -// FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`. impl EmissionGuarantee for ErrorGuaranteed { - fn emit_producing_guarantee(mut db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - // Contrast this with `emit_producing_nothing`. - match db.state { - // First `.emit()` call, the `&DiagCtxt` is still available. - DiagnosticBuilderState::Emittable(dcx) => { - db.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; - let guar = dcx.emit_diagnostic_without_consuming(&mut db.diagnostic); - - // Only allow a guarantee if the `level` wasn't switched to a - // non-error - the field isn't `pub`, but the whole `Diagnostic` - // can be overwritten with a new one, thanks to `DerefMut`. - assert!( - db.diagnostic.is_error(), - "emitted non-error ({:?}) diagnostic \ - from `DiagnosticBuilder`", - db.diagnostic.level, - ); - guar.unwrap() - } - // `.emit()` was previously called, disallowed from repeating it, - // but can take advantage of the previous `.emit()`'s guarantee - // still being applicable (i.e. as a form of idempotency). - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => { - // Only allow a guarantee if the `level` wasn't switched to a - // non-error - the field isn't `pub`, but the whole `Diagnostic` - // can be overwritten with a new one, thanks to `DerefMut`. - assert!( - db.diagnostic.is_error(), - "`DiagnosticBuilder`'s diagnostic \ - became non-error ({:?}), after original `.emit()`", - db.diagnostic.level, - ); - #[allow(deprecated)] - ErrorGuaranteed::unchecked_claim_error_was_emitted() - } - } + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_error_guaranteed() } } -// FIXME(eddyb) should there be a `Option` impl as well? impl EmissionGuarantee for () { fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { db.emit_producing_nothing(); @@ -219,12 +187,12 @@ macro_rules! forward { ) => { #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { - self.diagnostic.$n($($name),*); + self.diag.as_mut().unwrap().$n($($name),*); self } #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] pub fn $n_mv(mut self, $($name: $ty),*) -> Self { - self.diagnostic.$n($($name),*); + self.diag.as_mut().unwrap().$n($($name),*); self } }; @@ -234,13 +202,13 @@ impl Deref for DiagnosticBuilder<'_, G> { type Target = Diagnostic; fn deref(&self) -> &Diagnostic { - &self.diagnostic + self.diag.as_ref().unwrap() } } impl DerefMut for DiagnosticBuilder<'_, G> { fn deref_mut(&mut self) -> &mut Diagnostic { - &mut self.diagnostic + self.diag.as_mut().unwrap() } } @@ -254,13 +222,9 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// Creates a new `DiagnosticBuilder` with an already constructed /// diagnostic. #[track_caller] - pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diagnostic: Diagnostic) -> Self { + pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self { debug!("Created new diagnostic"); - Self { - state: DiagnosticBuilderState::Emittable(dcx), - diagnostic: Box::new(diagnostic), - _marker: PhantomData, - } + Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData } } /// Emit and consume the diagnostic. @@ -281,14 +245,10 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { self.emit() } - /// Cancel the diagnostic (a structured diagnostic must either be emitted or + /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or /// cancelled or it will panic when dropped). - /// - /// This method takes `self` by-value to disallow calling `.emit()` on it, - /// which may be expected to *guarantee* the emission of an error, either - /// at the time of the call, or through a prior `.emit()` call. pub fn cancel(mut self) { - self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; + self.diag = None; drop(self); } @@ -304,44 +264,21 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { } /// Converts the builder to a `Diagnostic` for later emission, - /// unless dcx has disabled such buffering, or `.emit()` was called. + /// unless dcx has disabled such buffering. pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> { - let dcx = match self.state { - // No `.emit()` calls, the `&DiagCtxt` is still available. - DiagnosticBuilderState::Emittable(dcx) => dcx, - // `.emit()` was previously called, nothing we can do. - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => { - return None; - } - }; - - if dcx.inner.lock().flags.dont_buffer_diagnostics - || dcx.inner.lock().flags.treat_err_as_bug.is_some() - { + let flags = self.dcx.inner.lock().flags; + if flags.dont_buffer_diagnostics || flags.treat_err_as_bug.is_some() { self.emit(); return None; } - // Take the `Diagnostic` by replacing it with a dummy. - let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::from("")); - let diagnostic = std::mem::replace(&mut *self.diagnostic, dummy); - - // Disable the ICE on `Drop`. - self.cancel(); + let diag = self.take_diag(); // Logging here is useful to help track down where in logs an error was // actually emitted. - debug!("buffer: diagnostic={:?}", diagnostic); + debug!("buffer: diag={:?}", diag); - Some((diagnostic, dcx)) - } - - /// Retrieves the [`DiagCtxt`] if available - pub fn dcx(&self) -> Option<&DiagCtxt> { - match self.state { - DiagnosticBuilderState::Emittable(dcx) => Some(dcx), - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None, - } + Some((diag, self.dcx)) } /// Buffers the diagnostic for later emission, @@ -494,30 +431,24 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { impl Debug for DiagnosticBuilder<'_, G> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.diagnostic.fmt(f) + self.diag.fmt(f) } } -/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled -/// or we emit a bug. +/// Destructor bomb: every `DiagnosticBuilder` must be consumed (emitted, +/// cancelled, etc.) or we emit a bug. impl Drop for DiagnosticBuilder<'_, G> { fn drop(&mut self) { - match self.state { - // No `.emit()` or `.cancel()` calls. - DiagnosticBuilderState::Emittable(dcx) => { - if !panicking() { - dcx.emit_diagnostic(Diagnostic::new( - Level::Bug, - DiagnosticMessage::from( - "the following error was constructed but not emitted", - ), - )); - dcx.emit_diagnostic_without_consuming(&mut self.diagnostic); - panic!("error was constructed but not emitted"); - } + match self.diag.take() { + Some(diag) if !panicking() => { + self.dcx.emit_diagnostic(Diagnostic::new( + Level::Bug, + DiagnosticMessage::from("the following error was constructed but not emitted"), + )); + self.dcx.emit_diagnostic(*diag); + panic!("error was constructed but not emitted"); } - // `.emit()` was previously called, or maybe we're during `.cancel()`. - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} + _ => {} } } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 4710722c7715..8411e188189d 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -7,6 +7,7 @@ #![feature(rustdoc_internals)] #![feature(array_windows)] #![feature(associated_type_defaults)] +#![feature(box_into_inner)] #![feature(extract_if)] #![feature(if_let_guard)] #![feature(let_chains)] diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index bde442049b1d..446f13feff08 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -181,8 +181,7 @@ pub(crate) struct UnsafeOpInUnsafeFn { impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn { #[track_caller] fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) { - let dcx = diag.dcx().expect("lint should not yet be emitted"); - let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter()); + let desc = diag.dcx.eagerly_translate_to_string(self.details.label(), [].into_iter()); diag.arg("details", desc); diag.span_label(self.details.span, self.details.label()); self.details.add_subdiagnostics(diag); From db09eb2d3accf8909ea2813ebb00c58c7f2fad64 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 8 Jan 2024 08:27:38 +1100 Subject: [PATCH 213/257] Remove `{DiagCtxt,DiagCtxtInner}::emit_diagnostic_without_consuming`. They are no longer used, because `{DiagCtxt,DiagCtxtInner}::emit_diagnostic` are used everywhere instead. This also means `track_diagnostic` can become consuming. --- compiler/rustc_errors/src/lib.rs | 30 ++++++----------------- compiler/rustc_interface/src/callbacks.rs | 2 +- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 8411e188189d..b97ec02675a6 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -509,11 +509,11 @@ pub enum StashKey { Cycle, } -fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) { - (*f)(d) +fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { + (*f)(diag) } -pub static TRACK_DIAGNOSTICS: AtomicRef = +pub static TRACK_DIAGNOSTICS: AtomicRef = AtomicRef::new(&(default_track_diagnostic as _)); #[derive(Copy, Clone, Default)] @@ -1074,17 +1074,8 @@ impl DiagCtxt { self.inner.borrow_mut().emitter.emit_diagnostic(&db); } - pub fn emit_diagnostic(&self, mut diagnostic: Diagnostic) -> Option { - self.emit_diagnostic_without_consuming(&mut diagnostic) - } - - // It's unfortunate this exists. `emit_diagnostic` is preferred, because it - // consumes the diagnostic, thus ensuring it is emitted just once. - pub(crate) fn emit_diagnostic_without_consuming( - &self, - diagnostic: &mut Diagnostic, - ) -> Option { - self.inner.borrow_mut().emit_diagnostic_without_consuming(diagnostic) + pub fn emit_diagnostic(&self, diagnostic: Diagnostic) -> Option { + self.inner.borrow_mut().emit_diagnostic(diagnostic) } #[track_caller] @@ -1273,13 +1264,6 @@ impl DiagCtxtInner { } fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option { - self.emit_diagnostic_without_consuming(&mut diagnostic) - } - - fn emit_diagnostic_without_consuming( - &mut self, - diagnostic: &mut Diagnostic, - ) -> Option { if matches!(diagnostic.level, Error | Fatal) && self.treat_err_as_bug() { diagnostic.level = Bug; } @@ -1335,7 +1319,7 @@ impl DiagCtxtInner { } let mut guaranteed = None; - (*TRACK_DIAGNOSTICS)(diagnostic, &mut |diagnostic| { + (*TRACK_DIAGNOSTICS)(diagnostic, &mut |mut diagnostic| { if let Some(ref code) = diagnostic.code { self.emitted_diagnostic_codes.insert(code.clone()); } @@ -1371,7 +1355,7 @@ impl DiagCtxtInner { ); } - self.emitter.emit_diagnostic(diagnostic); + self.emitter.emit_diagnostic(&diagnostic); if diagnostic.is_error() { self.deduplicated_err_count += 1; } else if let Warning(_) = diagnostic.level { diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index ef00ced67ffe..7458be2c86da 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -29,7 +29,7 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { /// This is a callback from `rustc_errors` as it cannot access the implicit state /// in `rustc_middle` otherwise. It is used when diagnostic messages are /// emitted and stores them in the current query, if there is one. -fn track_diagnostic(diagnostic: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) { +fn track_diagnostic(diagnostic: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { tls::with_context_opt(|icx| { if let Some(icx) = icx { if let Some(diagnostics) = icx.diagnostics { From 75df38e8165622e623fc24266ef4da49fd093933 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 8 Jan 2024 01:15:02 +0800 Subject: [PATCH 214/257] Fix 2 variable binding issues in let_underscore --- compiler/rustc_hir/src/hir.rs | 2 +- compiler/rustc_lint/src/let_underscore.rs | 5 +++ compiler/rustc_lint/src/lints.rs | 4 +- .../let_underscore/issue-119696-err-on-fn.rs | 21 +++++++++++ .../issue-119696-err-on-fn.stderr | 22 +++++++++++ .../let_underscore/issue-119697-extra-let.rs | 21 +++++++++++ .../issue-119697-extra-let.stderr | 37 +++++++++++++++++++ 7 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs create mode 100644 tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr create mode 100644 tests/ui/lint/let_underscore/issue-119697-extra-let.rs create mode 100644 tests/ui/lint/let_underscore/issue-119697-extra-let.stderr diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2c34fc13919b..cb7a766d0493 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2017,7 +2017,7 @@ pub enum LocalSource { AsyncFn, /// A desugared `.await`. AwaitDesugar, - /// A desugared `expr = expr`, where the LHS is a tuple, struct or array. + /// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression. /// The span is that of the `=` sign. AssignDesugar(Span), } diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 3eefd1b0e083..bdace8e01f6c 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -108,6 +108,10 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { if !matches!(local.pat.kind, hir::PatKind::Wild) { return; } + + if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { + return; + } if let Some(init) = local.init { let init_ty = cx.typeck_results().expr_ty(init); // If the type has a trivial Drop implementation, then it doesn't @@ -126,6 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { suggestion: local.pat.span, multi_suggestion_start: local.span.until(init.span), multi_suggestion_end: init.span.shrink_to_hi(), + is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)), }; if is_sync_lock { let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index ca6408bdf3dd..588b1541c4b7 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -950,6 +950,7 @@ pub struct NonBindingLetSub { pub suggestion: Span, pub multi_suggestion_start: Span, pub multi_suggestion_end: Span, + pub is_assign_desugar: bool, } impl AddToDiagnostic for NonBindingLetSub { @@ -960,10 +961,11 @@ impl AddToDiagnostic for NonBindingLetSub { rustc_errors::SubdiagnosticMessage, ) -> rustc_errors::SubdiagnosticMessage, { + let prefix = if self.is_assign_desugar { "let " } else { "" }; diag.span_suggestion_verbose( self.suggestion, fluent::lint_non_binding_let_suggestion, - "_unused", + format!("{prefix}_unused"), Applicability::MachineApplicable, ); diag.multipart_suggestion( diff --git a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs new file mode 100644 index 000000000000..8e15b4da35a4 --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs @@ -0,0 +1,21 @@ +// edition: 2021 + +#![deny(let_underscore_drop)] +fn main() { + let _ = foo(); //~ ERROR non-binding let on a type that implements `Drop` +} + +async fn from_config(_: Config) {} + +async fn foo() { + from_config(Config { + nickname: None, + ..Default::default() + }) + .await; +} + +#[derive(Default)] +struct Config { + nickname: Option>, +} diff --git a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr new file mode 100644 index 000000000000..86e521580b87 --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr @@ -0,0 +1,22 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/issue-119696-err-on-fn.rs:5:5 + | +LL | let _ = foo(); + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/issue-119696-err-on-fn.rs:3:9 + | +LL | #![deny(let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^ +help: consider binding to an unused variable to avoid immediately dropping the value + | +LL | let _unused = foo(); + | ~~~~~~~ +help: consider immediately dropping the value + | +LL | drop(foo()); + | ~~~~~ + + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.rs b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs new file mode 100644 index 000000000000..1dc80a123f61 --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs @@ -0,0 +1,21 @@ +#![deny(let_underscore_drop)] +#![feature(type_alias_impl_trait)] + +pub struct Foo { + /// This type must have nontrivial drop glue + field: String, +} + +pub type Tait = impl Sized; + +pub fn ice_cold(beverage: Tait) { + // Must destructure at least one field of `Foo` + let Foo { field } = beverage; + // boom + _ = field; //~ ERROR non-binding let on a type that implements `Drop` + + let _ = field; //~ ERROR non-binding let on a type that implements `Drop` +} + + +pub fn main() {} diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr new file mode 100644 index 000000000000..16df2c720eaf --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr @@ -0,0 +1,37 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/issue-119697-extra-let.rs:15:5 + | +LL | _ = field; + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/issue-119697-extra-let.rs:1:9 + | +LL | #![deny(let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^ +help: consider binding to an unused variable to avoid immediately dropping the value + | +LL | let _unused = field; + | ~~~~~~~~~~~ +help: consider immediately dropping the value + | +LL | drop(field); + | ~~~~~ + + +error: non-binding let on a type that implements `Drop` + --> $DIR/issue-119697-extra-let.rs:17:5 + | +LL | let _ = field; + | ^^^^^^^^^^^^^^ + | +help: consider binding to an unused variable to avoid immediately dropping the value + | +LL | let _unused = field; + | ~~~~~~~ +help: consider immediately dropping the value + | +LL | drop(field); + | ~~~~~ + + +error: aborting due to 2 previous errors + From 585a28561996be51739e9a0523371e6e0339cb56 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 15 Dec 2023 22:41:01 +1100 Subject: [PATCH 215/257] coverage: Test for column numbers involving non-ASCII characters --- tests/coverage/unicode.cov-map | 53 ++++++++++++++++++++++++++++++++++ tests/coverage/unicode.rs | 36 +++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 tests/coverage/unicode.cov-map create mode 100644 tests/coverage/unicode.rs diff --git a/tests/coverage/unicode.cov-map b/tests/coverage/unicode.cov-map new file mode 100644 index 000000000000..7648031f4df6 --- /dev/null +++ b/tests/coverage/unicode.cov-map @@ -0,0 +1,53 @@ +Function name: unicode::main +Raw bytes (67): 0x[01, 01, 09, 01, 05, 03, 05, 1e, 0d, 22, 09, 03, 05, 11, 1b, 1e, 0d, 22, 09, 03, 05, 09, 01, 0e, 01, 00, 0b, 05, 01, 09, 00, 0b, 03, 00, 0f, 00, 18, 05, 00, 19, 00, 24, 22, 02, 08, 00, 13, 09, 00, 17, 00, 22, 11, 00, 23, 02, 06, 1b, 02, 06, 00, 07, 17, 02, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 9 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(1) +- expression 2 operands: lhs = Expression(7, Sub), rhs = Counter(3) +- expression 3 operands: lhs = Expression(8, Sub), rhs = Counter(2) +- expression 4 operands: lhs = Expression(0, Add), rhs = Counter(1) +- expression 5 operands: lhs = Counter(4), rhs = Expression(6, Add) +- expression 6 operands: lhs = Expression(7, Sub), rhs = Counter(3) +- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(2) +- expression 8 operands: lhs = Expression(0, Add), rhs = Counter(1) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 14, 1) to (start + 0, 11) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 11) +- Code(Expression(0, Add)) at (prev + 0, 15) to (start + 0, 24) + = (c0 + c1) +- Code(Counter(1)) at (prev + 0, 25) to (start + 0, 36) +- Code(Expression(8, Sub)) at (prev + 2, 8) to (start + 0, 19) + = ((c0 + c1) - c1) +- Code(Counter(2)) at (prev + 0, 23) to (start + 0, 34) +- Code(Counter(4)) at (prev + 0, 35) to (start + 2, 6) +- Code(Expression(6, Add)) at (prev + 2, 6) to (start + 0, 7) + = ((((c0 + c1) - c1) - c2) + c3) +- Code(Expression(5, Add)) at (prev + 2, 5) to (start + 1, 2) + = (c4 + ((((c0 + c1) - c1) - c2) + c3)) + +Function name: unicode::サビ +Raw bytes (9): 0x[01, 01, 00, 01, 01, 1e, 12, 00, 14] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 30, 18) to (start + 0, 20) + +Function name: unicode::他 (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 1e, 15, 00, 1f] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 30, 21) to (start + 0, 31) + +Function name: unicode::申し訳ございません +Raw bytes (9): 0x[01, 01, 00, 01, 01, 18, 01, 02, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 24, 1) to (start + 2, 2) + diff --git a/tests/coverage/unicode.rs b/tests/coverage/unicode.rs new file mode 100644 index 000000000000..3335d3af458a --- /dev/null +++ b/tests/coverage/unicode.rs @@ -0,0 +1,36 @@ +// edition: 2021 +// ignore-mode-coverage-run - `llvm-cov` fails due to incorrectly-split UTF-8 +// llvm-cov-flags: --use-color + +// Check that column numbers are denoted in bytes, so that they don't cause +// `llvm-cov` to fail or emit malformed output. +// +// Note that when `llvm-cov` prints ^ arrows on a subsequent line, it simply +// inserts one space character for each "column", with no understanding of +// Unicode or character widths. So those arrows will tend to be misaligned +// for non-ASCII source code, regardless of whether column numbers are code +// points or bytes. + +fn main() { + for _İ in 'А'..='Я' { /* Я */ } + + if 申し訳ございません() && 申し訳ございません() { + println!("true"); + } + + サビ(); +} + +fn 申し訳ございません() -> bool { + std::hint::black_box(false) +} + +macro_rules! macro_that_defines_a_function { + (fn $名:ident () $体:tt) => { + fn $名 () $体 fn 他 () {} + } +} + +macro_that_defines_a_function! { + fn サビ() {} +} From 88f5759ace32abed2bd3dd81ee2aa15d6878675c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 6 Jan 2024 11:22:34 +1100 Subject: [PATCH 216/257] coverage: Allow `make_code_region` to fail --- .../rustc_mir_transform/src/coverage/mod.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index aa7b6b02f74e..98de9d829d28 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -107,6 +107,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ); let mappings = self.create_mappings(&coverage_spans, &coverage_counters); + if mappings.is_empty() { + // No spans could be converted into valid mappings, so skip this function. + debug!("no spans could be converted into valid mappings; skipping"); + return; + } + self.inject_coverage_statements(bcb_has_coverage_spans, &coverage_counters); self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { @@ -148,9 +154,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // Flatten the spans into individual term/span pairs. .flat_map(|(term, spans)| spans.iter().map(move |&span| (term, span))) // Convert each span to a code region, and create the final mapping. - .map(|(term, span)| { - let code_region = make_code_region(source_map, file_name, span, body_span); - Mapping { term, code_region } + .filter_map(|(term, span)| { + let code_region = make_code_region(source_map, file_name, span, body_span)?; + Some(Mapping { term, code_region }) }) .collect::>() } @@ -258,7 +264,7 @@ fn make_code_region( file_name: Symbol, span: Span, body_span: Span, -) -> CodeRegion { +) -> Option { debug!( "Called make_code_region(file_name={}, span={}, body_span={})", file_name, @@ -280,13 +286,13 @@ fn make_code_region( start_line = source_map.doctest_offset_line(&file.name, start_line); end_line = source_map.doctest_offset_line(&file.name, end_line); } - CodeRegion { + Some(CodeRegion { file_name, start_line: start_line as u32, start_col: start_col as u32, end_line: end_line as u32, end_col: end_col as u32, - } + }) } fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { From f2dbebafad629316edf37f9fc6a91633d9d1a702 Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Wed, 29 Nov 2023 12:49:48 +0700 Subject: [PATCH 217/257] Improved support of collapse_debuginfo attribute for macros. --- .../src/debuginfo/line_info.rs | 10 +- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 17 +- compiler/rustc_middle/src/ty/mod.rs | 22 +-- compiler/rustc_span/src/hygiene.rs | 34 +++- compiler/rustc_span/src/lib.rs | 7 - ...ollapse-debuginfo-in-non-collapse-macro.rs | 124 +++++++++++++ tests/debuginfo/skip_second_statement.rs | 168 +++++++++++++++++ .../skip_second_statement_collapse.rs | 170 ++++++++++++++++++ 8 files changed, 513 insertions(+), 39 deletions(-) create mode 100644 tests/debuginfo/collapse-debuginfo-in-non-collapse-macro.rs create mode 100644 tests/debuginfo/skip_second_statement.rs create mode 100644 tests/debuginfo/skip_second_statement_collapse.rs diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs index 6230ca15d6e1..d1b21d0a0b6c 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -68,15 +68,7 @@ impl DebugContext { // In order to have a good line stepping behavior in debugger, we overwrite debug // locations of macro expansions with that of the outermost expansion site (when the macro is // annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided). - let span = if tcx.should_collapse_debuginfo(span) { - span - } else { - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - rustc_span::hygiene::walk_chain(span, function_span.ctxt()) - }; - + let span = tcx.collapsed_debuginfo(span, function_span); match tcx.sess.source_map().lookup_line(span.lo()) { Ok(SourceFileAndLine { sf: file, line }) => { let line_pos = file.lines()[line]; diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 14915e816ee9..48f3f4f25228 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -228,21 +228,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// In order to have a good line stepping behavior in debugger, we overwrite debug /// locations of macro expansions with that of the outermost expansion site (when the macro is /// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided). - fn adjust_span_for_debugging(&self, mut span: Span) -> Span { + fn adjust_span_for_debugging(&self, span: Span) -> Span { // Bail out if debug info emission is not enabled. if self.debug_context.is_none() { return span; } - - if self.cx.tcx().should_collapse_debuginfo(span) { - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - // Use span of the outermost expansion site, while keeping the original lexical scope. - span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt()); - } - - span + // Walk up the macro expansion chain until we reach a non-expanded span. + // We also stop at the function body level because no line stepping can occur + // at the level above that. + // Use span of the outermost expansion site, while keeping the original lexical scope. + self.cx.tcx().collapsed_debuginfo(span, self.mir.span) } fn spill_operand_to_stack( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9f1ff4538aa2..8983fb7e40c7 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -50,7 +50,7 @@ use rustc_session::lint::LintBuffer; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{ExpnId, ExpnKind, Span}; +use rustc_span::{hygiene, ExpnId, ExpnKind, Span}; use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx}; pub use rustc_target::abi::{ReprFlags, ReprOptions}; pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx}; @@ -2515,21 +2515,21 @@ impl<'tcx> TyCtxt<'tcx> { (ident, scope) } - /// Returns `true` if the debuginfo for `span` should be collapsed to the outermost expansion - /// site. Only applies when `Span` is the result of macro expansion. + /// Returns corrected span if the debuginfo for `span` should be collapsed to the outermost + /// expansion site (with collapse_debuginfo attribute if the corresponding feature enabled). + /// Only applies when `Span` is the result of macro expansion. /// /// - If the `collapse_debuginfo` feature is enabled then debuginfo is not collapsed by default - /// and only when a macro definition is annotated with `#[collapse_debuginfo]`. + /// and only when a (some enclosing) macro definition is annotated with `#[collapse_debuginfo]`. /// - If `collapse_debuginfo` is not enabled, then debuginfo is collapsed by default. /// /// When `-Zdebug-macros` is provided then debuginfo will never be collapsed. - pub fn should_collapse_debuginfo(self, span: Span) -> bool { - !self.sess.opts.unstable_opts.debug_macros - && if self.features().collapse_debuginfo { - span.in_macro_expansion_with_collapse_debuginfo() - } else { - span.from_expansion() - } + pub fn collapsed_debuginfo(self, span: Span, upto: Span) -> Span { + if self.sess.opts.unstable_opts.debug_macros || !span.from_expansion() { + return span; + } + let collapse_debuginfo_enabled = self.features().collapse_debuginfo; + hygiene::walk_chain_collapsed(span, upto, collapse_debuginfo_enabled) } #[inline] diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index b717229b68df..d9ed85dbb988 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -443,18 +443,46 @@ impl HygieneData { } fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span { + let orig_span = span; debug!("walk_chain({:?}, {:?})", span, to); debug!("walk_chain: span ctxt = {:?}", span.ctxt()); - while span.from_expansion() && span.ctxt() != to { + while span.ctxt() != to && span.from_expansion() { let outer_expn = self.outer_expn(span.ctxt()); debug!("walk_chain({:?}): outer_expn={:?}", span, outer_expn); let expn_data = self.expn_data(outer_expn); debug!("walk_chain({:?}): expn_data={:?}", span, expn_data); span = expn_data.call_site; } + debug!("walk_chain: for span {:?} >>> return span = {:?}", orig_span, span); span } + // We need to walk up and update return span if we meet macro instantiation to be collapsed + fn walk_chain_collapsed( + &self, + mut span: Span, + to: Span, + collapse_debuginfo_enabled: bool, + ) -> Span { + let orig_span = span; + let mut ret_span = span; + + debug!("walk_chain_collapsed({:?}, {:?})", span, to); + debug!("walk_chain_collapsed: span ctxt = {:?}", span.ctxt()); + while !span.eq_ctxt(to) && span.from_expansion() { + let outer_expn = self.outer_expn(span.ctxt()); + debug!("walk_chain_collapsed({:?}): outer_expn={:?}", span, outer_expn); + let expn_data = self.expn_data(outer_expn); + debug!("walk_chain_collapsed({:?}): expn_data={:?}", span, expn_data); + span = expn_data.call_site; + if !collapse_debuginfo_enabled || expn_data.collapse_debuginfo { + ret_span = span; + } + } + debug!("walk_chain_collapsed: for span {:?} >>> return span = {:?}", orig_span, ret_span); + ret_span + } + fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option { let mut scope = None; while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) { @@ -571,6 +599,10 @@ pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { HygieneData::with(|data| data.walk_chain(span, to)) } +pub fn walk_chain_collapsed(span: Span, to: Span, collapse_debuginfo_enabled: bool) -> Span { + HygieneData::with(|hdata| hdata.walk_chain_collapsed(span, to, collapse_debuginfo_enabled)) +} + pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { // The new contexts that need updating are at the end of the list and have `$crate` as a name. let (len, to_update) = HygieneData::with(|data| { diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 8f64eed9a870..d42c420ea9c4 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -568,13 +568,6 @@ impl Span { self.ctxt() != SyntaxContext::root() } - /// Returns `true` if `span` originates in a macro's expansion where debuginfo should be - /// collapsed. - pub fn in_macro_expansion_with_collapse_debuginfo(self) -> bool { - let outer_expn = self.ctxt().outer_expn_data(); - matches!(outer_expn.kind, ExpnKind::Macro(..)) && outer_expn.collapse_debuginfo - } - /// Returns `true` if `span` originates in a derive-macro's expansion. pub fn in_derive_expansion(self) -> bool { matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _)) diff --git a/tests/debuginfo/collapse-debuginfo-in-non-collapse-macro.rs b/tests/debuginfo/collapse-debuginfo-in-non-collapse-macro.rs new file mode 100644 index 000000000000..d9500c3641ef --- /dev/null +++ b/tests/debuginfo/collapse-debuginfo-in-non-collapse-macro.rs @@ -0,0 +1,124 @@ +// ignore-lldb +#![feature(collapse_debuginfo)] + +// Test that statement, skipped/added/reordered by macros, is correctly processed in debuginfo. +// When nested macros instantiations are tagged with collapse_debuginfo attribute, +// debug info should be corrected to the first outer macro instantiation +// without collapse_debuginfo attribute. +// collapse_debuginfo feature enabled. + +// compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1_pre[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_in_proxy[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_rem_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1_pre[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_in_proxy[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_add_macro[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder_call2[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1_pre[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_in_proxy[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-command:continue + +#[inline(never)] +fn myprintln_impl(text: &str) { + println!("{}", text) +} + +#[collapse_debuginfo] +macro_rules! myprintln { + ($($arg:tt)*) => {{ + myprintln_impl($($arg)*); + }}; +} + +macro_rules! proxy_println { + ($($arg:tt)*) => {{ + myprintln!($($arg)*); // #loc_in_proxy + }}; +} + +// Macro accepts 3 statements and removes the 2nd statement +macro_rules! remove_second_statement { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 } +} + +macro_rules! add_second_statement { + ($s1:stmt; $s3:stmt;) => { + $s1 + call2(); // #loc_add_macro + $s3 + } +} + +macro_rules! reorder_statements { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s2 $s3 $s1 } +} + +fn call1() { + let rv = 0; // #loc_call1_pre + proxy_println!("one"); // #loc_call1 +} + +fn call2() { + proxy_println!("two"); // #loc_call2 +} + +fn call3() { + proxy_println!("three"); // #loc_call3 +} + +fn main() { + let ret = 0; // #break, step should go to call1 + remove_second_statement! { // #loc_rem_hdr + call1(); // #loc_rem_call1 + call2(); // #loc_rem_call2 + call3(); // #loc_rem_call3 + } + add_second_statement! { // #loc_add_hdr + call1(); // #loc_add_call1 + call3(); // #loc_add_call3 + } + reorder_statements! { // #loc_reorder_hdr + call1(); // #loc_reorder_call1 + call2(); // #loc_reorder_call2 + call3(); // #loc_reorder_call3 + } + std::process::exit(ret); // #loc_exit +} diff --git a/tests/debuginfo/skip_second_statement.rs b/tests/debuginfo/skip_second_statement.rs new file mode 100644 index 000000000000..535b54747631 --- /dev/null +++ b/tests/debuginfo/skip_second_statement.rs @@ -0,0 +1,168 @@ +// ignore-lldb + +// Test that statement, skipped/added/reordered by macros, is correctly processed in debuginfo. +// Performed step-over and step-into debug stepping through call statements. +// collapse_debuginfo feature disabled. + +// compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_rem2_call3[...] +// gdb-command:step 2 +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_after_rem[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_hdr[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_add2_hdr[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call2[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-check:[...]#loc_add2_call3[...] +// gdb-command:step 2 +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call2[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call2[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call2[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call3[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call3[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-command:continue + +#[inline(never)] +fn myprintln_impl(text: &str) { + println!("{}", text) +} + +macro_rules! myprintln { + ($($arg:tt)*) => {{ + myprintln_impl($($arg)*); + }}; +} + +// Macro accepts 3 statements and removes the 2nd statement +macro_rules! remove_second_statement { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 } +} + +macro_rules! add_second_statement { + ($s1:stmt; $s3:stmt;) => { + $s1 + call2(); // #loc_add_macro + $s3 + } +} + +macro_rules! reorder_statements { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s2 $s3 $s1 } +} + +fn call1() { + myprintln!("one"); // #loc_call1 +} + +fn call2() { + myprintln!("two"); // #loc_call2 +} + +fn call3() { + (||{ + myprintln!("three") // #loc_call3_println + })(); // #loc_call3 +} + +fn main() { + let ret = 0; // #break, step should go to call1 + remove_second_statement! { // #loc_rem1_hdr + call1(); // #loc_rem1_call1, breakpoint should set to call1, step should go call3 + call2(); // #loc_rem1_call2, breakpoint should set to call3 + call3(); // #loc_rem1_call3 + } + remove_second_statement! { // #loc_rem2_hdr + call1(); // #loc_rem2_call1, breakpoint should set to call1, step should go call3 + call2(); // #loc_rem2_call2, breakpoint should set to call3 + call3(); // #loc_rem2_call3, breakpoint should set to call3 + } + myprintln!("After remove_second_statement test"); // #loc_after_rem + + add_second_statement! { // #loc_add1_hdr + call1(); // #loc_add1_call1 + call3(); // #loc_add1_call3 + } + add_second_statement! { // #loc_add2_hdr + call1(); // #loc_add2_call1 + call3(); // #loc_add2_call3 + } + + reorder_statements! { // #loc_reorder1_hdr + call1(); // #loc_reorder1_call1 + call2(); // #loc_reorder1_call2 + call3(); // #loc_reorder1_call3 + } + reorder_statements! { // #loc_reorder2_hdr + call1(); // #loc_reorder2_call1 + call2(); // #loc_reorder2_call2 + call3(); // #loc_reorder2_call3 + } + + std::process::exit(ret); // #loc_exit +} diff --git a/tests/debuginfo/skip_second_statement_collapse.rs b/tests/debuginfo/skip_second_statement_collapse.rs new file mode 100644 index 000000000000..a0557ca9feec --- /dev/null +++ b/tests/debuginfo/skip_second_statement_collapse.rs @@ -0,0 +1,170 @@ +// ignore-lldb +#![feature(collapse_debuginfo)] + +// Test that statement, skipped/added/reordered by macros, is correctly processed in debuginfo +// Performed step-over and step-into debug stepping through call statements. +// collapse_debuginfo feature enabled. + +// compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_rem2_call3[...] +// gdb-command:step 2 +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_after_rem[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add_macro[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_add_macro[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call2[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-check:[...]#loc_add2_call3[...] +// gdb-command:step 2 +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call2[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call2[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call2[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call3[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call3[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-command:continue + +#[inline(never)] +fn myprintln_impl(text: &str) { + println!("{}", text) +} + +#[collapse_debuginfo] +macro_rules! myprintln { + ($($arg:tt)*) => {{ + myprintln_impl($($arg)*); + }}; +} + +// Macro accepts 3 statements and removes the 2nd statement +macro_rules! remove_second_statement { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 } +} + +macro_rules! add_second_statement { + ($s1:stmt; $s3:stmt;) => { + $s1 + call2(); // #loc_add_macro + $s3 + } +} + +macro_rules! reorder_statements { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s2 $s3 $s1 } +} + +fn call1() { + myprintln!("one"); // #loc_call1 +} + +fn call2() { + myprintln!("two"); // #loc_call2 +} + +fn call3() { + (||{ + myprintln!("three") // #loc_call3_println + })(); // #loc_call3 +} + +fn main() { + let ret = 0; // #break, step should go to call1 + remove_second_statement! { // #loc_rem1_hdr + call1(); // #loc_rem1_call1, breakpoint should set to call1, step should go call3 + call2(); // #loc_rem1_call2, breakpoint should set to call3 + call3(); // #loc_rem1_call3 + } + remove_second_statement! { // #loc_rem2_hdr + call1(); // #loc_rem2_call1, breakpoint should set to call1, step should go call3 + call2(); // #loc_rem2_call2, breakpoint should set to call3 + call3(); // #loc_rem2_call3, breakpoint should set to call3 + } + myprintln!("After remove_second_statement test"); // #loc_after_rem + + add_second_statement! { // #loc_add1_hdr + call1(); // #loc_add1_call1 + call3(); // #loc_add1_call3 + } + add_second_statement! { // #loc_add2_hdr + call1(); // #loc_add2_call1 + call3(); // #loc_add2_call3 + } + + reorder_statements! { // #loc_reorder1_hdr + call1(); // #loc_reorder1_call1 + call2(); // #loc_reorder1_call2 + call3(); // #loc_reorder1_call3 + } + reorder_statements! { // #loc_reorder2_hdr + call1(); // #loc_reorder2_call1 + call2(); // #loc_reorder2_call2 + call3(); // #loc_reorder2_call3 + } + + std::process::exit(ret); // #loc_exit +} From 6971e9332d9a7fb1567a6df0f30d03452f505ed3 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 15 Dec 2023 14:12:46 +1100 Subject: [PATCH 218/257] coverage: `llvm-cov` expects column numbers to be bytes, not code points --- .../rustc_mir_transform/src/coverage/mod.rs | 74 +++++++++++++++---- compiler/rustc_mir_transform/src/lib.rs | 1 + tests/coverage/unicode.cov-map | 22 +++--- tests/coverage/unicode.coverage | 40 ++++++++++ tests/coverage/unicode.rs | 2 +- 5 files changed, 112 insertions(+), 27 deletions(-) create mode 100644 tests/coverage/unicode.coverage diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 98de9d829d28..dcd7014f4fc9 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -23,7 +23,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::SourceMap; -use rustc_span::{Span, Symbol}; +use rustc_span::{BytePos, Pos, RelativeBytePos, Span, Symbol}; /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen @@ -258,7 +258,16 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb data.statements.insert(0, statement); } -/// Convert the Span into its file name, start line and column, and end line and column +/// Convert the Span into its file name, start line and column, and end line and column. +/// +/// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by +/// the compiler, these column numbers are denoted in **bytes**, because that's what +/// LLVM's `llvm-cov` tool expects to see in coverage maps. +/// +/// Returns `None` if the conversion failed for some reason. This shouldn't happen, +/// but it's hard to rule out entirely (especially in the presence of complex macros +/// or other expansions), and if it does happen then skipping a span or function is +/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid. fn make_code_region( source_map: &SourceMap, file_name: Symbol, @@ -272,20 +281,55 @@ fn make_code_region( source_map.span_to_diagnostic_string(body_span) ); - let (file, mut start_line, mut start_col, mut end_line, mut end_col) = - source_map.span_to_location_info(span); - if span.hi() == span.lo() { - // Extend an empty span by one character so the region will be counted. - if span.hi() == body_span.hi() { - start_col = start_col.saturating_sub(1); - } else { - end_col = start_col + 1; - } - }; - if let Some(file) = file { - start_line = source_map.doctest_offset_line(&file.name, start_line); - end_line = source_map.doctest_offset_line(&file.name, end_line); + let lo = span.lo(); + let hi = span.hi(); + + let file = source_map.lookup_source_file(lo); + if !file.contains(hi) { + debug!(?span, ?file, ?lo, ?hi, "span crosses multiple files; skipping"); + return None; } + + // Column numbers need to be in bytes, so we can't use the more convenient + // `SourceMap` methods for looking up file coordinates. + let rpos_and_line_and_byte_column = |pos: BytePos| -> Option<(RelativeBytePos, usize, usize)> { + let rpos = file.relative_position(pos); + let line_index = file.lookup_line(rpos)?; + let line_start = file.lines()[line_index]; + // Line numbers and column numbers are 1-based, so add 1 to each. + Some((rpos, line_index + 1, (rpos - line_start).to_usize() + 1)) + }; + + let (lo_rpos, mut start_line, mut start_col) = rpos_and_line_and_byte_column(lo)?; + let (hi_rpos, mut end_line, mut end_col) = rpos_and_line_and_byte_column(hi)?; + + // If the span is empty, try to expand it horizontally by one character's + // worth of bytes, so that it is more visible in `llvm-cov` reports. + // We do this after resolving line/column numbers, so that empty spans at the + // end of a line get an extra column instead of wrapping to the next line. + if span.is_empty() + && body_span.contains(span) + && let Some(src) = &file.src + { + // Prefer to expand the end position, if it won't go outside the body span. + if hi < body_span.hi() { + let hi_rpos = hi_rpos.to_usize(); + let nudge_bytes = src.ceil_char_boundary(hi_rpos + 1) - hi_rpos; + end_col += nudge_bytes; + } else if lo > body_span.lo() { + let lo_rpos = lo_rpos.to_usize(); + let nudge_bytes = lo_rpos - src.floor_char_boundary(lo_rpos - 1); + // Subtract the nudge, but don't go below column 1. + start_col = start_col.saturating_sub(nudge_bytes).max(1); + } + // If neither nudge could be applied, stick with the empty span coordinates. + } + + // Apply an offset so that code in doctests has correct line numbers. + // FIXME(#79417): Currently we have no way to offset doctest _columns_. + start_line = source_map.doctest_offset_line(&file.name, start_line); + end_line = source_map.doctest_offset_line(&file.name, end_line); + Some(CodeRegion { file_name, start_line: start_line as u32, diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index f5f51c0ec8ad..2c1602dadc17 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -9,6 +9,7 @@ #![feature(min_specialization)] #![feature(never_type)] #![feature(option_get_or_insert_default)] +#![feature(round_char_boundary)] #![feature(trusted_step)] #![feature(try_blocks)] #![feature(yeet_expr)] diff --git a/tests/coverage/unicode.cov-map b/tests/coverage/unicode.cov-map index 7648031f4df6..cd40194a0831 100644 --- a/tests/coverage/unicode.cov-map +++ b/tests/coverage/unicode.cov-map @@ -1,5 +1,5 @@ Function name: unicode::main -Raw bytes (67): 0x[01, 01, 09, 01, 05, 03, 05, 1e, 0d, 22, 09, 03, 05, 11, 1b, 1e, 0d, 22, 09, 03, 05, 09, 01, 0e, 01, 00, 0b, 05, 01, 09, 00, 0b, 03, 00, 0f, 00, 18, 05, 00, 19, 00, 24, 22, 02, 08, 00, 13, 09, 00, 17, 00, 22, 11, 00, 23, 02, 06, 1b, 02, 06, 00, 07, 17, 02, 05, 01, 02] +Raw bytes (67): 0x[01, 01, 09, 01, 05, 03, 05, 1e, 0d, 22, 09, 03, 05, 11, 1b, 1e, 0d, 22, 09, 03, 05, 09, 01, 0e, 01, 00, 0b, 05, 01, 09, 00, 0c, 03, 00, 10, 00, 1b, 05, 00, 1c, 00, 28, 22, 02, 08, 00, 25, 09, 00, 29, 00, 46, 11, 00, 47, 02, 06, 1b, 02, 06, 00, 07, 17, 02, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 9 @@ -14,34 +14,34 @@ Number of expressions: 9 - expression 8 operands: lhs = Expression(0, Add), rhs = Counter(1) Number of file 0 mappings: 9 - Code(Counter(0)) at (prev + 14, 1) to (start + 0, 11) -- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 11) -- Code(Expression(0, Add)) at (prev + 0, 15) to (start + 0, 24) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) +- Code(Expression(0, Add)) at (prev + 0, 16) to (start + 0, 27) = (c0 + c1) -- Code(Counter(1)) at (prev + 0, 25) to (start + 0, 36) -- Code(Expression(8, Sub)) at (prev + 2, 8) to (start + 0, 19) +- Code(Counter(1)) at (prev + 0, 28) to (start + 0, 40) +- Code(Expression(8, Sub)) at (prev + 2, 8) to (start + 0, 37) = ((c0 + c1) - c1) -- Code(Counter(2)) at (prev + 0, 23) to (start + 0, 34) -- Code(Counter(4)) at (prev + 0, 35) to (start + 2, 6) +- Code(Counter(2)) at (prev + 0, 41) to (start + 0, 70) +- Code(Counter(4)) at (prev + 0, 71) to (start + 2, 6) - Code(Expression(6, Add)) at (prev + 2, 6) to (start + 0, 7) = ((((c0 + c1) - c1) - c2) + c3) - Code(Expression(5, Add)) at (prev + 2, 5) to (start + 1, 2) = (c4 + ((((c0 + c1) - c1) - c2) + c3)) Function name: unicode::サビ -Raw bytes (9): 0x[01, 01, 00, 01, 01, 1e, 12, 00, 14] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 1e, 14, 00, 18] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 30, 18) to (start + 0, 20) +- Code(Counter(0)) at (prev + 30, 20) to (start + 0, 24) Function name: unicode::他 (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 1e, 15, 00, 1f] +Raw bytes (9): 0x[01, 01, 00, 01, 00, 1e, 19, 00, 25] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Zero) at (prev + 30, 21) to (start + 0, 31) +- Code(Zero) at (prev + 30, 25) to (start + 0, 37) Function name: unicode::申し訳ございません Raw bytes (9): 0x[01, 01, 00, 01, 01, 18, 01, 02, 02] diff --git a/tests/coverage/unicode.coverage b/tests/coverage/unicode.coverage new file mode 100644 index 000000000000..b284a557d575 --- /dev/null +++ b/tests/coverage/unicode.coverage @@ -0,0 +1,40 @@ + LL| |// edition: 2021 + LL| |// ignore-windows - we can't force `llvm-cov` to use ANSI escapes on Windows + LL| |// llvm-cov-flags: --use-color + LL| | + LL| |// Check that column numbers are denoted in bytes, so that they don't cause + LL| |// `llvm-cov` to fail or emit malformed output. + LL| |// + LL| |// Note that when `llvm-cov` prints ^ arrows on a subsequent line, it simply + LL| |// inserts one space character for each "column", with no understanding of + LL| |// Unicode or character widths. So those arrows will tend to be misaligned + LL| |// for non-ASCII source code, regardless of whether column numbers are code + LL| |// points or bytes. + LL| | + LL| 1|fn main() { + LL| 33| for _İ in 'А'..='Я' { /* Я */ } + ^32 ^32 + LL| | + LL| 1| if 申し訳ございません() && 申し訳ございません() { + ^0 + LL| 0| println!("true"); + LL| 1| } + LL| | + LL| 1| サビ(); + LL| 1|} + LL| | + LL| 1|fn 申し訳ございません() -> bool { + LL| 1| std::hint::black_box(false) + LL| 1|} + LL| | + LL| |macro_rules! macro_that_defines_a_function { + LL| | (fn $名:ident () $体:tt) => { + LL| 1| fn $名 () $体 fn 他 () {} + ^0 + LL| | } + LL| |} + LL| | + LL| |macro_that_defines_a_function! { + LL| | fn サビ() {} + LL| |} + diff --git a/tests/coverage/unicode.rs b/tests/coverage/unicode.rs index 3335d3af458a..dfc5ea69dd23 100644 --- a/tests/coverage/unicode.rs +++ b/tests/coverage/unicode.rs @@ -1,5 +1,5 @@ // edition: 2021 -// ignore-mode-coverage-run - `llvm-cov` fails due to incorrectly-split UTF-8 +// ignore-windows - we can't force `llvm-cov` to use ANSI escapes on Windows // llvm-cov-flags: --use-color // Check that column numbers are denoted in bytes, so that they don't cause From 43ce53375c976ad31629282e17ca7861c0d23a2c Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Mon, 8 Jan 2024 12:54:06 +0000 Subject: [PATCH 219/257] Add riscv32imafc-esp-espidf target for the ESP32-P4. --- compiler/rustc_target/src/spec/mod.rs | 2 ++ .../spec/targets/riscv32imafc_esp_espidf.rs | 30 +++++++++++++++++++ src/doc/rustc/src/platform-support.md | 1 + src/doc/rustc/src/platform-support/esp-idf.md | 1 + 4 files changed, 34 insertions(+) create mode 100644 compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 9d25388b90fd..8e26327196a1 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1601,6 +1601,8 @@ supported_targets! { ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), ("riscv32imc-esp-espidf", riscv32imc_esp_espidf), ("riscv32imac-esp-espidf", riscv32imac_esp_espidf), + ("riscv32imafc-esp-espidf", riscv32imafc_esp_espidf), + ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), ("riscv32imafc-unknown-none-elf", riscv32imafc_unknown_none_elf), ("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs new file mode 100644 index 000000000000..6c7c920bd180 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs @@ -0,0 +1,30 @@ +use crate::spec::{cvs, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "espidf".into(), + env: "newlib".into(), + vendor: "espressif".into(), + linker: Some("riscv32-esp-elf-gcc".into()), + cpu: "generic-rv32".into(), + + max_atomic_width: Some(32), + atomic_cas: true, + + llvm_abiname: "ilp32f".into(), + features: "+m,+a,+c,+f".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 33a678a31d6a..2ddf5737fbd9 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -323,6 +323,7 @@ target | std | host | notes [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF +[`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia diff --git a/src/doc/rustc/src/platform-support/esp-idf.md b/src/doc/rustc/src/platform-support/esp-idf.md index 8f630fa152c4..1f8d98598099 100644 --- a/src/doc/rustc/src/platform-support/esp-idf.md +++ b/src/doc/rustc/src/platform-support/esp-idf.md @@ -19,6 +19,7 @@ The target names follow this format: `$ARCH-esp-espidf`, where `$ARCH` specifies | `riscv32imc-esp-espidf` | [ESP32-C3](https://www.espressif.com/en/products/socs/esp32-c3) | `v4.3` | | `riscv32imac-esp-espidf` | [ESP32-C6](https://www.espressif.com/en/products/socs/esp32-c6) | `v5.1` | | `riscv32imac-esp-espidf` | [ESP32-H2](https://www.espressif.com/en/products/socs/esp32-h2) | `v5.1` | +| `riscv32imafc-esp-espidf`| [ESP32-P4](https://www.espressif.com/en/news/ESP32-P4) | `v5.2` | It is recommended to use the latest ESP-IDF stable release if possible. From 8aa7dd06f6e50621dc10f9f9490681be8a45876f Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Fri, 5 Jan 2024 16:22:49 +0300 Subject: [PATCH 220/257] enable RUSTC_BOOTSTRAP on panic=abort mir-opt test Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/synthetic_targets.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index d2c65b740dad..9acdcaeb517b 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -59,6 +59,11 @@ fn create_synthetic_target( let mut cmd = Command::new(builder.rustc(compiler)); cmd.arg("--target").arg(base.rustc_target_arg()); cmd.args(["-Zunstable-options", "--print", "target-spec-json"]); + + // If `rust.channel` is set to either beta or stable, rustc will complain that + // we cannot use nightly features. So `RUSTC_BOOTSTRAP` is needed here. + cmd.env("RUSTC_BOOTSTRAP", "1"); + cmd.stdout(Stdio::piped()); let output = cmd.spawn().unwrap().wait_with_output().unwrap(); From 26c71cbcf1a9bce6ceb962d753c467d098f63cf6 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Fri, 5 Jan 2024 17:23:58 +0300 Subject: [PATCH 221/257] detect user-specified custom targets in compiletest Signed-off-by: onur-ozkan --- src/tools/compiletest/src/common.rs | 45 +++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index e85f6319936b..c45c0b3c6522 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -479,6 +479,7 @@ impl TargetCfgs { let mut targets: HashMap = serde_json::from_str(&rustc_output( config, &["--print=all-target-specs-json", "-Zunstable-options"], + Default::default(), )) .unwrap(); @@ -491,16 +492,33 @@ impl TargetCfgs { let mut all_families = HashSet::new(); let mut all_pointer_widths = HashSet::new(); - // Handle custom target specs, which are not included in `--print=all-target-specs-json`. - if config.target.ends_with(".json") { - targets.insert( - config.target.clone(), - serde_json::from_str(&rustc_output( - config, - &["--print=target-spec-json", "-Zunstable-options", "--target", &config.target], - )) - .unwrap(), - ); + // If current target is not included in the `--print=all-target-specs-json` output, + // we check whether it is a custom target from the user or a synthetic target from bootstrap. + if !targets.contains_key(&config.target) { + let mut envs: HashMap = HashMap::new(); + + if let Ok(t) = std::env::var("RUST_TARGET_PATH") { + envs.insert("RUST_TARGET_PATH".into(), t); + } + + // This returns false only when the target is neither a synthetic target + // nor a custom target from the user, indicating it is most likely invalid. + if config.target.ends_with(".json") || !envs.is_empty() { + targets.insert( + config.target.clone(), + serde_json::from_str(&rustc_output( + config, + &[ + "--print=target-spec-json", + "-Zunstable-options", + "--target", + &config.target, + ], + envs, + )) + .unwrap(), + ); + } } for (target, cfg) in targets.iter() { @@ -545,7 +563,9 @@ impl TargetCfgs { // code below extracts them from `--print=cfg`: make sure to only override fields that can // actually be changed with `-C` flags. for config in - rustc_output(config, &["--print=cfg", "--target", &config.target]).trim().lines() + rustc_output(config, &["--print=cfg", "--target", &config.target], Default::default()) + .trim() + .lines() { let (name, value) = config .split_once("=\"") @@ -624,11 +644,12 @@ pub enum Endian { Big, } -fn rustc_output(config: &Config, args: &[&str]) -> String { +fn rustc_output(config: &Config, args: &[&str], envs: HashMap) -> String { let mut command = Command::new(&config.rustc_path); add_dylib_path(&mut command, iter::once(&config.compile_lib_path)); command.args(&config.target_rustcflags).args(args); command.env("RUSTC_BOOTSTRAP", "1"); + command.envs(envs); let output = match command.output() { Ok(output) => output, From b888e2f82b9dbe81875f50d13adbc0271a9401ff Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sun, 7 Jan 2024 11:40:09 +0300 Subject: [PATCH 222/257] fix the incorrect target on stage1 ui-fulldeps tests Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/test.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 92140b00da84..254adec2eab2 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1596,8 +1596,13 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the // running compiler in stage 2 when plugins run. let stage_id = if suite == "ui-fulldeps" && compiler.stage == 1 { - compiler = builder.compiler(compiler.stage - 1, target); - format!("stage{}-{}", compiler.stage + 1, target) + // At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead finding + // an incorrect compiler path on cross-targets, as the stage 0 beta compiler is always equal + // to `build.build` in the configuration. + let build = builder.build.build; + + compiler = builder.compiler(compiler.stage - 1, build); + format!("stage{}-{}", compiler.stage + 1, build) } else { format!("stage{}-{}", compiler.stage, target) }; From e44b11f6952659e7cb4e66ee3b5ff47bb7bc6c31 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 7 Jan 2024 22:53:50 +0000 Subject: [PATCH 223/257] ~const trait or projection bounds do not imply non-const bounds --- compiler/rustc_hir_analysis/src/bounds.rs | 18 ---- .../src/collect/predicates_of.rs | 38 ++------ .../assoc-type-const-bound-usage.rs | 2 +- .../assoc-type-const-bound-usage.stderr | 16 ++-- .../const-fns-are-early-bound.rs | 86 +++++++++++++++++++ .../effects/minicore.rs | 17 ++-- .../effects/project.rs | 9 +- .../effects/project.stderr | 65 ++++++++++++++ .../trait-where-clause-const.stderr | 34 +++++--- .../unsatisfied-const-trait-bound.rs | 2 +- .../unsatisfied-const-trait-bound.stderr | 10 +-- 11 files changed, 214 insertions(+), 83 deletions(-) create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.rs create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.stderr diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index b6688e0ce29e..b69f679880db 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -45,24 +45,6 @@ impl<'tcx> Bounds<'tcx> { polarity: ty::ImplPolarity, ) { self.push_trait_bound_inner(tcx, trait_ref, span, polarity); - - // push a non-const (`host = true`) version of the bound if it is `~const`. - if tcx.features().effects - && let Some(host_effect_idx) = tcx.generics_of(trait_ref.def_id()).host_effect_index - && trait_ref.skip_binder().args.const_at(host_effect_idx) != tcx.consts.true_ - { - let generics = tcx.generics_of(trait_ref.def_id()); - let Some(host_index) = generics.host_effect_index else { return }; - let trait_ref = trait_ref.map_bound(|mut trait_ref| { - trait_ref.args = - tcx.mk_args_from_iter(trait_ref.args.iter().enumerate().map(|(n, arg)| { - if host_index == n { tcx.consts.true_.into() } else { arg } - })); - trait_ref - }); - - self.push_trait_bound_inner(tcx, trait_ref, span, polarity); - } } fn push_trait_bound_inner( diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 41520718aa8d..ab9ed6ef98d9 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -11,7 +11,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate}; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_span::{Span, DUMMY_SP}; /// Returns a list of all type predicates (explicit and implicit) for the definition with /// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus @@ -38,38 +38,12 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic // an obligation and instead be skipped. Otherwise we'd use // `tcx.def_span(def_id);` let span = rustc_span::DUMMY_SP; - let non_const_bound = if tcx.features().effects && tcx.has_attr(def_id, sym::const_trait) { - // when `Self` is a const trait, also add `Self: Trait<.., true>` as implied bound, - // because only implementing `Self: Trait<.., false>` is currently not possible. - Some(( - ty::TraitRef::new( - tcx, - def_id, - ty::GenericArgs::for_item(tcx, def_id, |param, _| { - if param.is_host_effect() { - tcx.consts.true_.into() - } else { - tcx.mk_param_from_def(param) - } - }), - ) - .to_predicate(tcx), + + result.predicates = + tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( + ty::TraitRef::identity(tcx, def_id).to_predicate(tcx), span, - )) - } else { - None - }; - result.predicates = tcx.arena.alloc_from_iter( - result - .predicates - .iter() - .copied() - .chain(std::iter::once(( - ty::TraitRef::identity(tcx, def_id).to_predicate(tcx), - span, - ))) - .chain(non_const_bound), - ); + )))); } debug!("predicates_of(def_id={:?}) = {:?}", def_id, result); result diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs index f41c1051fcea..16b717bc1813 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs @@ -9,7 +9,7 @@ trait Foo { } const fn foo() { - ::Assoc::foo(); + ::Assoc::foo(); } fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr index 1b88839984f4..268e337ee93e 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr @@ -6,15 +6,17 @@ LL | type Assoc: ~const Foo; | = note: this item cannot have `~const` trait bounds -error[E0308]: mismatched types - --> $DIR/assoc-type-const-bound-usage.rs:12:5 +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/assoc-type-const-bound-usage.rs:12:6 | -LL | ::Assoc::foo(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `host`, found `true` +LL | ::Assoc::foo(); + | ^ the trait `Foo` is not implemented for `T` | - = note: expected constant `host` - found constant `true` +help: consider further restricting this bound + | +LL | const fn foo() { + | +++++ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.rs new file mode 100644 index 000000000000..f3480fcc9eed --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.rs @@ -0,0 +1,86 @@ +// check-pass + +#![crate_type = "lib"] +#![allow(internal_features)] +#![no_std] +#![no_core] +#![feature( + auto_traits, + const_trait_impl, + effects, + lang_items, + no_core, + staged_api, + unboxed_closures +)] +#![stable(feature = "minicore", since = "1.0.0")] + +fn test() { + fn is_const_fn(_: F) + where + F: const FnOnce<()>, + { + } + + const fn foo() {} + + is_const_fn(foo); +} + +/// ---------------------------------------------------------------------- /// +/// Const fn trait definitions + +#[const_trait] +#[lang = "fn"] +#[rustc_paren_sugar] +trait Fn: ~const FnMut { + extern "rust-call" fn call(&self, args: Args) -> Self::Output; +} + +#[const_trait] +#[lang = "fn_mut"] +#[rustc_paren_sugar] +trait FnMut: ~const FnOnce { + extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; +} + +#[const_trait] +#[lang = "fn_once"] +#[rustc_paren_sugar] +trait FnOnce { + #[lang = "fn_once_output"] + type Output; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; +} + +/// ---------------------------------------------------------------------- /// +/// All this other stuff needed for core. Unrelated to test. + +#[lang = "destruct"] +#[const_trait] +trait Destruct {} + +#[lang = "freeze"] +unsafe auto trait Freeze {} + +#[lang = "drop"] +#[const_trait] +trait Drop { + fn drop(&mut self); +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +#[lang = "tuple_trait"] +trait Tuple {} + +#[lang = "receiver"] +trait Receiver {} + +impl Receiver for &T {} + +impl Receiver for &mut T {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs index 59fb48e794cc..84d9bcd7ac9f 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs @@ -40,7 +40,7 @@ const fn bar() { #[lang = "Try"] #[const_trait] -trait Try: FromResidual { +trait Try: FromResidual { type Output; type Residual; @@ -53,7 +53,7 @@ trait Try: FromResidual { // FIXME // #[const_trait] -trait FromResidual::Residual> { +trait FromResidual::Residual> { #[lang = "from_residual"] fn from_residual(residual: R) -> Self; } @@ -519,9 +519,14 @@ extern "rust-intrinsic" { called_in_const: F, called_at_rt: G, ) -> RET - /* where clauses enforced by built-in method confirmation: where - F: const FnOnce, - G: FnOnce, - */; + F: const FnOnce, + G: FnOnce; +} + +fn test_const_eval_select() { + const fn const_fn() {} + fn rt_fn() {} + + unsafe { const_eval_select((), const_fn, rt_fn); } } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.rs index b30d7743edfc..e22eed3e0ef5 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.rs @@ -1,7 +1,12 @@ -// check-pass +// known-bug: #110395 +// FIXME: effects + #![feature(const_trait_impl, effects)] -pub trait Owo::T> {} +// This fails because `~const Uwu` doesn't imply (non-const) `Uwu`. + +// FIXME: #[const_trait] +pub trait Owo::T> {} #[const_trait] pub trait Uwu: Owo { diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.stderr new file mode 100644 index 000000000000..eac3ee9e4e22 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.stderr @@ -0,0 +1,65 @@ +error[E0277]: the trait bound `Self: Uwu` is not satisfied + --> $DIR/project.rs:12:1 + | +LL | pub trait Uwu: Owo { + | ^^^^^^^^^^^^^^^^^^ the trait `Uwu` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | pub trait Uwu: Owo + Uwu { + | +++++ + +error[E0277]: the trait bound `Self: Uwu` is not satisfied + --> $DIR/project.rs:12:1 + | +LL | / pub trait Uwu: Owo { +LL | | type T; +LL | | } + | |_^ the trait `Uwu` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | pub trait Uwu: Owo + Uwu { + | +++++ + +error[E0277]: the trait bound `Self: Uwu` is not satisfied + --> $DIR/project.rs:12:16 + | +LL | pub trait Uwu: Owo { + | ^^^ the trait `Uwu` is not implemented for `Self` + | +note: required by a bound in `Owo` + --> $DIR/project.rs:9:15 + | +LL | pub trait Owo::T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Owo` +help: consider further restricting `Self` + | +LL | pub trait Uwu: Owo + Uwu { + | +++++ + +error[E0277]: the trait bound `Self: Uwu` is not satisfied + --> $DIR/project.rs:13:5 + | +LL | type T; + | ^^^^^^ the trait `Uwu` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | pub trait Uwu: Owo + Uwu { + | +++++ + +error[E0277]: the trait bound `Self: Uwu` is not satisfied + --> $DIR/project.rs:13:5 + | +LL | type T; + | ^^^^^^^ the trait `Uwu` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | pub trait Uwu: Owo + Uwu { + | +++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr index 2a9647da7823..0141dcfb06f6 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr @@ -1,21 +1,35 @@ -error[E0308]: mismatched types +error[E0277]: the trait bound `T: ~const Bar` is not satisfied --> $DIR/trait-where-clause-const.rs:21:5 | LL | T::b(); - | ^^^^^^ expected `host`, found `true` + | ^ the trait `~const Bar` is not implemented for `T` | - = note: expected constant `host` - found constant `true` +note: required by a bound in `Foo::b` + --> $DIR/trait-where-clause-const.rs:15:24 + | +LL | fn b() where Self: ~const Bar; + | ^^^^^^^^^^ required by this bound in `Foo::b` +help: consider further restricting this bound + | +LL | const fn test1() { + | ++++++++++++ -error[E0308]: mismatched types - --> $DIR/trait-where-clause-const.rs:23:5 +error[E0277]: the trait bound `T: ~const Bar` is not satisfied + --> $DIR/trait-where-clause-const.rs:23:12 | LL | T::c::(); - | ^^^^^^^^^^^ expected `host`, found `true` + | ^ the trait `~const Bar` is not implemented for `T` | - = note: expected constant `host` - found constant `true` +note: required by a bound in `Foo::c` + --> $DIR/trait-where-clause-const.rs:16:13 + | +LL | fn c(); + | ^^^^^^^^^^ required by this bound in `Foo::c` +help: consider further restricting this bound + | +LL | const fn test1() { + | ++++++++++++ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs index 62a7b3123784..8f441410c178 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs @@ -30,4 +30,4 @@ fn accept0(_: Container<{ T::make() }>) {} // FIXME(effects): Instead of suggesting `+ const Trait`, suggest // changing `~const Trait` to `const Trait`. const fn accept1(_: Container<{ T::make() }>) {} -//~^ ERROR the trait bound `T: const Trait` is not satisfied +//~^ ERROR mismatched types diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr index 2fb4fc1aa2b8..258f95b5c4a8 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr @@ -7,16 +7,14 @@ LL | fn accept0(_: Container<{ T::make() }>) {} = note: expected constant `false` found constant `true` -error[E0277]: the trait bound `T: const Trait` is not satisfied +error[E0308]: mismatched types --> $DIR/unsatisfied-const-trait-bound.rs:32:50 | LL | const fn accept1(_: Container<{ T::make() }>) {} - | ^ the trait `const Trait` is not implemented for `T` + | ^^^^^^^^^ expected `false`, found `host` | -help: consider further restricting this bound - | -LL | const fn accept1(_: Container<{ T::make() }>) {} - | +++++++++++++ + = note: expected constant `false` + found constant `host` error[E0277]: the trait bound `Ty: const Trait` is not satisfied --> $DIR/unsatisfied-const-trait-bound.rs:20:15 From 8abf133c4b158a0c1b720c28cb25c482490c104f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 5 Dec 2023 17:29:36 +0000 Subject: [PATCH 224/257] Make inductive cycles in coherence ambiguous always --- compiler/rustc_lint/src/lib.rs | 5 ++ compiler/rustc_lint_defs/src/builtin.rs | 40 ------------ .../src/traits/coherence.rs | 64 ++----------------- .../src/traits/select/mod.rs | 20 +++--- .../warn-when-cycle-is-error-in-coherence.rs | 7 +- ...rn-when-cycle-is-error-in-coherence.stderr | 46 ++----------- 6 files changed, 27 insertions(+), 155 deletions(-) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 76c630fc456a..a9996e4a155b 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -513,6 +513,11 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, see PR #117984 \ for more information", ); + store.register_removed( + "coinductive_overlap_in_coherence", + "converted into hard error, see PR #118649 \ + for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 19da51b7dcfa..c74d4bb651c0 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -26,7 +26,6 @@ declare_lint_pass! { BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, CENUM_IMPL_DROP_CAST, COHERENCE_LEAK_CHECK, - COINDUCTIVE_OVERLAP_IN_COHERENCE, CONFLICTING_REPR_HINTS, CONST_EVALUATABLE_UNCHECKED, CONST_ITEM_MUTATION, @@ -4367,45 +4366,6 @@ declare_lint! { @feature_gate = sym::type_privacy_lints; } -declare_lint! { - /// The `coinductive_overlap_in_coherence` lint detects impls which are currently - /// considered not overlapping, but may be considered to overlap if support for - /// coinduction is added to the trait solver. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(coinductive_overlap_in_coherence)] - /// - /// trait CyclicTrait {} - /// impl CyclicTrait for T {} - /// - /// trait Trait {} - /// impl Trait for T {} - /// // conflicting impl with the above - /// impl Trait for u8 {} - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// We have two choices for impl which satisfy `u8: Trait`: the blanket impl - /// for generic `T`, and the direct impl for `u8`. These two impls nominally - /// overlap, since we can infer `T = u8` in the former impl, but since the where - /// clause `u8: CyclicTrait` would end up resulting in a cycle (since it depends - /// on itself), the blanket impl is not considered to hold for `u8`. This will - /// change in a future release. - pub COINDUCTIVE_OVERLAP_IN_COHERENCE, - Deny, - "impls that are not considered to overlap may be considered to \ - overlap in the future", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #114040 ", - }; -} - declare_lint! { /// The `unknown_or_malformed_diagnostic_attributes` lint detects unrecognized or otherwise malformed /// diagnostic attributes. diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 533fe32f70d8..ecbb92ca5b99 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -10,7 +10,7 @@ use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor use crate::solve::{deeply_normalize_for_diagnostics, inspect}; use crate::traits::engine::TraitEngineExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs}; +use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::structural_normalize::StructurallyNormalizeExt; use crate::traits::NormalizeExt; use crate::traits::SkipLeakCheck; @@ -31,7 +31,6 @@ use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; -use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; use std::fmt::Debug; @@ -197,7 +196,7 @@ fn overlap<'tcx>( .intercrate(true) .with_next_trait_solver(tcx.next_trait_solver_in_coherence()) .build(); - let selcx = &mut SelectionContext::new(&infcx); + let selcx = &mut SelectionContext::with_treat_inductive_cycle_as_ambig(&infcx); if track_ambiguity_causes.is_yes() { selcx.enable_tracking_intercrate_ambiguity_causes(); } @@ -224,61 +223,10 @@ fn overlap<'tcx>( ); if overlap_mode.use_implicit_negative() { - for mode in [TreatInductiveCycleAs::Ambig, TreatInductiveCycleAs::Recur] { - if let Some(failing_obligation) = selcx.with_treat_inductive_cycle_as(mode, |selcx| { - impl_intersection_has_impossible_obligation(selcx, &obligations) - }) { - if matches!(mode, TreatInductiveCycleAs::Recur) { - let first_local_impl = impl1_header - .impl_def_id - .as_local() - .or(impl2_header.impl_def_id.as_local()) - .expect("expected one of the impls to be local"); - infcx.tcx.struct_span_lint_hir( - COINDUCTIVE_OVERLAP_IN_COHERENCE, - infcx.tcx.local_def_id_to_hir_id(first_local_impl), - infcx.tcx.def_span(first_local_impl), - format!( - "implementations {} will conflict in the future", - match impl1_header.trait_ref { - Some(trait_ref) => { - let trait_ref = infcx.resolve_vars_if_possible(trait_ref); - format!( - "of `{}` for `{}`", - trait_ref.print_trait_sugared(), - trait_ref.self_ty() - ) - } - None => format!( - "for `{}`", - infcx.resolve_vars_if_possible(impl1_header.self_ty) - ), - }, - ), - |lint| { - lint.note( - "impls that are not considered to overlap may be considered to \ - overlap in the future", - ) - .span_label( - infcx.tcx.def_span(impl1_header.impl_def_id), - "the first impl is here", - ) - .span_label( - infcx.tcx.def_span(impl2_header.impl_def_id), - "the second impl is here", - ); - lint.note(format!( - "`{}` may be considered to hold in future releases, \ - causing the impls to overlap", - infcx.resolve_vars_if_possible(failing_obligation.predicate) - )); - }, - ); - } - - return None; - } + if let Some(_failing_obligation) = + impl_intersection_has_impossible_obligation(selcx, &obligations) + { + return None; } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c45925295ee7..61fe2c8efe36 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -239,20 +239,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - // Sets the `TreatInductiveCycleAs` mode temporarily in the selection context - pub fn with_treat_inductive_cycle_as( - &mut self, - treat_inductive_cycle: TreatInductiveCycleAs, - f: impl FnOnce(&mut Self) -> T, - ) -> T { + pub fn with_treat_inductive_cycle_as_ambig( + infcx: &'cx InferCtxt<'tcx>, + ) -> SelectionContext<'cx, 'tcx> { // Should be executed in a context where caching is disabled, // otherwise the cache is poisoned with the temporary result. - assert!(self.is_intercrate()); - let treat_inductive_cycle = - std::mem::replace(&mut self.treat_inductive_cycle, treat_inductive_cycle); - let value = f(self); - self.treat_inductive_cycle = treat_inductive_cycle; - value + assert!(infcx.intercrate); + SelectionContext { + treat_inductive_cycle: TreatInductiveCycleAs::Ambig, + ..SelectionContext::new(infcx) + } } pub fn with_query_mode( diff --git a/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.rs b/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.rs index 01f7d6ce9010..c50bbcec5215 100644 --- a/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.rs +++ b/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.rs @@ -1,18 +1,15 @@ -#![deny(coinductive_overlap_in_coherence)] - use std::borrow::Borrow; use std::cmp::Ordering; use std::marker::PhantomData; #[derive(PartialEq, Default)] +//~^ ERROR conflicting implementations of trait `PartialEq>` for type `Interval<_>` pub(crate) struct Interval(PhantomData); // This impl overlaps with the `derive` unless we reject the nested // `Interval: PartialOrd>` candidate which results -// in a - currently inductive - cycle. +// in a -- currently inductive -- cycle. impl PartialEq for Interval -//~^ ERROR implementations of `PartialEq>` for `Interval<_>` will conflict in the future -//~| WARN this was previously accepted by the compiler but is being phased out where T: Borrow, Q: ?Sized + PartialOrd, diff --git a/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.stderr b/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.stderr index 4535b6f68115..af4dbfcad0e2 100644 --- a/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.stderr +++ b/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.stderr @@ -1,51 +1,17 @@ -error: implementations of `PartialEq>` for `Interval<_>` will conflict in the future - --> $DIR/warn-when-cycle-is-error-in-coherence.rs:13:1 +error[E0119]: conflicting implementations of trait `PartialEq>` for type `Interval<_>` + --> $DIR/warn-when-cycle-is-error-in-coherence.rs:5:10 | LL | #[derive(PartialEq, Default)] - | --------- the second impl is here + | ^^^^^^^^^ conflicting implementation for `Interval<_>` ... LL | / impl PartialEq for Interval -LL | | -LL | | LL | | where LL | | T: Borrow, LL | | Q: ?Sized + PartialOrd, - | |___________________________^ the first impl is here + | |___________________________- first implementation here | - = 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 #114040 - = note: impls that are not considered to overlap may be considered to overlap in the future - = note: `Interval<_>: PartialOrd` may be considered to hold in future releases, causing the impls to overlap -note: the lint level is defined here - --> $DIR/warn-when-cycle-is-error-in-coherence.rs:1:9 - | -LL | #![deny(coinductive_overlap_in_coherence)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error -Future incompatibility report: Future breakage diagnostic: -error: implementations of `PartialEq>` for `Interval<_>` will conflict in the future - --> $DIR/warn-when-cycle-is-error-in-coherence.rs:13:1 - | -LL | #[derive(PartialEq, Default)] - | --------- the second impl is here -... -LL | / impl PartialEq for Interval -LL | | -LL | | -LL | | where -LL | | T: Borrow, -LL | | Q: ?Sized + PartialOrd, - | |___________________________^ the first impl is here - | - = 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 #114040 - = note: impls that are not considered to overlap may be considered to overlap in the future - = note: `Interval<_>: PartialOrd` may be considered to hold in future releases, causing the impls to overlap -note: the lint level is defined here - --> $DIR/warn-when-cycle-is-error-in-coherence.rs:1:9 - | -LL | #![deny(coinductive_overlap_in_coherence)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - +For more information about this error, try `rustc --explain E0119`. From 760673e97d48e131b87ec738cb7106c5f8abe55e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 8 Jan 2024 01:07:14 +0000 Subject: [PATCH 225/257] Remove logic in one_bound in astconv that prefers non-const bounds --- .../rustc_hir_analysis/src/astconv/mod.rs | 35 ++----------------- .../const-impl-trait.rs | 2 ++ .../const-impl-trait.stderr | 21 +++++------ 3 files changed, 13 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 092df257dbfa..70f0c9bc4325 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1032,7 +1032,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name) }); - let Some(mut bound) = matching_candidates.next() else { + let Some(bound) = matching_candidates.next() else { let reported = self.complain_about_assoc_item_not_found( all_candidates, &ty_param_name.to_string(), @@ -1046,38 +1046,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; debug!(?bound); - // look for a candidate that is not the same as our first bound, disregarding - // whether the bound is const. - let mut next_cand = matching_candidates.next(); - while let Some(mut bound2) = next_cand { - debug!(?bound2); - if bound2.bound_vars() != bound.bound_vars() { - break; - } - - let generics = tcx.generics_of(bound.def_id()); - let Some(host_index) = generics.host_effect_index else { break }; - - // always return the bound that contains the host param. - if let ty::ConstKind::Param(_) = bound2.skip_binder().args.const_at(host_index).kind() { - (bound, bound2) = (bound2, bound); - } - - let unconsted_args = bound - .skip_binder() - .args - .iter() - .enumerate() - .map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg }); - - if unconsted_args.eq(bound2.skip_binder().args.iter()) { - next_cand = matching_candidates.next(); - } else { - break; - } - } - - if let Some(bound2) = next_cand { + if let Some(bound2) = matching_candidates.next() { debug!(?bound2); let assoc_kind_str = assoc_kind_str(assoc_kind); diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs index 14d306fc31f6..9d579e67a4be 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs @@ -1,4 +1,6 @@ // known-bug: #110395 +// Broken until we have `&T: const Deref` impl in stdlib + #![allow(incomplete_features)] #![feature( associated_type_bounds, diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr index d0ca1b19ad17..d4be71f2f466 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `()` with `()` - --> $DIR/const-impl-trait.rs:35:17 + --> $DIR/const-impl-trait.rs:37:17 | LL | assert!(cmp(&())); | --- ^^^ no implementation for `() == ()` @@ -9,23 +9,20 @@ LL | assert!(cmp(&())); = help: the trait `const PartialEq` is not implemented for `()` = help: the trait `PartialEq` is implemented for `()` note: required by a bound in `cmp` - --> $DIR/const-impl-trait.rs:12:23 + --> $DIR/const-impl-trait.rs:14:23 | LL | const fn cmp(a: &impl ~const PartialEq) -> bool { | ^^^^^^^^^^^^^^^^ required by this bound in `cmp` -error[E0277]: can't compare `&impl ~const PartialEq` with `&impl ~const PartialEq` - --> $DIR/const-impl-trait.rs:13:7 +error[E0369]: binary operation `==` cannot be applied to type `&impl ~const PartialEq` + --> $DIR/const-impl-trait.rs:15:7 | LL | a == a - | ^^ no implementation for `&impl ~const PartialEq == &impl ~const PartialEq` - | - = help: the trait `~const PartialEq<&impl ~const PartialEq>` is not implemented for `&impl ~const PartialEq` -help: consider dereferencing both sides of the expression - | -LL | *a == *a - | + + + | - ^^ - &impl ~const PartialEq + | | + | &impl ~const PartialEq error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0369. +For more information about an error, try `rustc --explain E0277`. From 5b041abc8cfae5076e9187e127945de113c92fed Mon Sep 17 00:00:00 2001 From: Arthur Carcano Date: Thu, 13 Jul 2023 12:46:14 +0200 Subject: [PATCH 226/257] A more efficient slice comparison implementation for T: !BytewiseEq The previous implementation was not optimized properly by the compiler, which didn't leverage the fact that both length were equal. --- library/core/src/slice/cmp.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 075347b80d03..8a8d634c0072 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -60,7 +60,17 @@ where return false; } - self.iter().zip(other.iter()).all(|(x, y)| x == y) + // Implemented as explicit indexing rather + // than zipped iterators for performance reasons. + // See PR https://github.com/rust-lang/rust/pull/116846 + for idx in 0..self.len() { + // bound checks are optimized away + if self[idx] != other[idx] { + return false; + } + } + + true } } From 506c06636b5fe4b59558cbb08cf87809175648b8 Mon Sep 17 00:00:00 2001 From: Mads Ravn Date: Mon, 8 Jan 2024 19:41:01 +0100 Subject: [PATCH 227/257] Removing redudant note from parse error --- compiler/rustc_parse_format/src/lib.rs | 2 +- tests/ui/fmt/format-string-wrong-order.stderr | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index eff54ac93089..1e1d45d6f702 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -893,7 +893,7 @@ impl<'a> Parser<'a> { 0, ParseError { description: "expected format parameter to occur after `:`".to_owned(), - note: Some(format!("`{}` comes after `:`.", alignment)), + note: None, label: format!("expected `{}` to occur after `:`", alignment).to_owned(), span: pos.to(pos), secondary_label: None, diff --git a/tests/ui/fmt/format-string-wrong-order.stderr b/tests/ui/fmt/format-string-wrong-order.stderr index 221228012d6a..3ef07720c153 100644 --- a/tests/ui/fmt/format-string-wrong-order.stderr +++ b/tests/ui/fmt/format-string-wrong-order.stderr @@ -55,24 +55,18 @@ error: invalid format string: expected format parameter to occur after `:` | LL | format!("Hello {<5:}!", "x"); | ^ expected `<` to occur after `:` in format string - | - = note: `<` comes after `:`. error: invalid format string: expected format parameter to occur after `:` --> $DIR/format-string-wrong-order.rs:17:21 | LL | format!("Hello {^5:}!", "x"); | ^ expected `^` to occur after `:` in format string - | - = note: `^` comes after `:`. error: invalid format string: expected format parameter to occur after `:` --> $DIR/format-string-wrong-order.rs:19:21 | LL | format!("Hello {>5:}!", "x"); | ^ expected `>` to occur after `:` in format string - | - = note: `>` comes after `:`. error: aborting due to 9 previous errors From e61be1bfae982b991828d3edd64a0eaea8bc2d55 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 8 Jan 2024 12:00:40 -0700 Subject: [PATCH 228/257] rustdoc-search: reuse empty map/array in function signatures Map is implemented as a pointer to a mutable object. Rustdoc never mutates function signatures after constructing them, but the JS engine doesn't know that. To save a bunch of memory, use a single immutable map for every decoded type object with no bindings or generics. --- src/librustdoc/html/static/js/search.js | 26 ++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index a5e2bc1c7afd..5bd375a86a78 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2717,9 +2717,25 @@ ${item.displayPath}${name}\ * @return {Array} */ function buildItemSearchTypeAll(types, lowercasePaths) { - return types.map(type => buildItemSearchType(type, lowercasePaths)); + return types.length > 0 ? + types.map(type => buildItemSearchType(type, lowercasePaths)) : + EMPTY_GENERICS_ARRAY; } + /** + * Empty, immutable map used in item search types with no bindings. + * + * @type {Map>} + */ + const EMPTY_BINDINGS_MAP = new Map(); + + /** + * Empty, immutable map used in item search types with no bindings. + * + * @type {Array} + */ + const EMPTY_GENERICS_ARRAY = []; + /** * Converts a single type. * @@ -2732,15 +2748,15 @@ ${item.displayPath}${name}\ let pathIndex, generics, bindings; if (typeof type === "number") { pathIndex = type; - generics = []; - bindings = new Map(); + generics = EMPTY_GENERICS_ARRAY; + bindings = EMPTY_BINDINGS_MAP; } else { pathIndex = type[PATH_INDEX_DATA]; generics = buildItemSearchTypeAll( type[GENERICS_DATA], lowercasePaths ); - if (type.length > BINDINGS_DATA) { + if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { bindings = new Map(type[BINDINGS_DATA].map(binding => { const [assocType, constraints] = binding; // Associated type constructors are represented sloppily in rustdoc's @@ -2759,7 +2775,7 @@ ${item.displayPath}${name}\ ]; })); } else { - bindings = new Map(); + bindings = EMPTY_BINDINGS_MAP; } } if (pathIndex < 0) { From 684aa2c9d11d9e818ea65f7e4ebd194b97318ac7 Mon Sep 17 00:00:00 2001 From: David Koloski Date: Wed, 6 Dec 2023 18:25:13 +0000 Subject: [PATCH 229/257] Add support for shell argfiles --- Cargo.lock | 1 + compiler/rustc_driver_impl/Cargo.toml | 1 + compiler/rustc_driver_impl/src/args.rs | 107 +++++++++++++++--- compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_session/src/options.rs | 2 + .../src/compiler-flags/shell-argfiles.md | 11 ++ src/tools/tidy/src/deps.rs | 1 + src/tools/tidy/src/ui_tests.rs | 6 +- .../shell-argfiles-badquotes-windows.rs | 11 ++ .../shell-argfiles-badquotes-windows.stderr | 2 + .../shell-argfiles-badquotes.args | 1 + .../shell-argfiles-badquotes.rs | 12 ++ .../shell-argfiles-badquotes.stderr | 2 + .../shell-argfiles-via-argfile-shell.args | 1 + .../shell-argfiles-via-argfile.args | 1 + .../shell-argfiles-via-argfile.rs | 10 ++ tests/ui/shell-argfiles/shell-argfiles.args | 3 + tests/ui/shell-argfiles/shell-argfiles.rs | 19 ++++ 18 files changed, 175 insertions(+), 17 deletions(-) create mode 100644 src/doc/unstable-book/src/compiler-flags/shell-argfiles.md create mode 100644 tests/ui/shell-argfiles/shell-argfiles-badquotes-windows.rs create mode 100644 tests/ui/shell-argfiles/shell-argfiles-badquotes-windows.stderr create mode 100644 tests/ui/shell-argfiles/shell-argfiles-badquotes.args create mode 100644 tests/ui/shell-argfiles/shell-argfiles-badquotes.rs create mode 100644 tests/ui/shell-argfiles/shell-argfiles-badquotes.stderr create mode 100644 tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args create mode 100644 tests/ui/shell-argfiles/shell-argfiles-via-argfile.args create mode 100644 tests/ui/shell-argfiles/shell-argfiles-via-argfile.rs create mode 100644 tests/ui/shell-argfiles/shell-argfiles.args create mode 100644 tests/ui/shell-argfiles/shell-argfiles.rs diff --git a/Cargo.lock b/Cargo.lock index b8192e333fe9..c7fa0b567528 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3738,6 +3738,7 @@ dependencies = [ "rustc_trait_selection", "rustc_ty_utils", "serde_json", + "shlex", "time", "tracing", "windows", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 490429845538..97a7dfef3b39 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -50,6 +50,7 @@ rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } serde_json = "1.0.59" +shlex = "1.0" time = { version = "0.3", default-features = false, features = ["alloc", "formatting"] } tracing = { version = "0.1.35" } # tidy-alphabetical-end diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs index b7407f5a508e..5dfd37a6da4d 100644 --- a/compiler/rustc_driver_impl/src/args.rs +++ b/compiler/rustc_driver_impl/src/args.rs @@ -5,18 +5,92 @@ use std::io; use rustc_session::EarlyDiagCtxt; -fn arg_expand(arg: String) -> Result, Error> { - if let Some(path) = arg.strip_prefix('@') { - let file = match fs::read_to_string(path) { - Ok(file) => file, - Err(ref err) if err.kind() == io::ErrorKind::InvalidData => { - return Err(Error::Utf8Error(Some(path.to_string()))); +/// Expands argfiles in command line arguments. +#[derive(Default)] +struct Expander { + shell_argfiles: bool, + next_is_unstable_option: bool, + expanded: Vec, +} + +impl Expander { + /// Handles the next argument. If the argument is an argfile, it is expanded + /// inline. + fn arg(&mut self, arg: &str) -> Result<(), Error> { + if let Some(argfile) = arg.strip_prefix('@') { + match argfile.split_once(':') { + Some(("shell", path)) if self.shell_argfiles => { + shlex::split(&Self::read_file(path)?) + .ok_or_else(|| Error::ShellParseError(path.to_string()))? + .into_iter() + .for_each(|arg| self.push(arg)); + } + _ => { + let contents = Self::read_file(argfile)?; + contents.lines().for_each(|arg| self.push(arg.to_string())); + } } - Err(err) => return Err(Error::IOError(path.to_string(), err)), - }; - Ok(file.lines().map(ToString::to_string).collect()) - } else { - Ok(vec![arg]) + } else { + self.push(arg.to_string()); + } + + Ok(()) + } + + /// Adds a command line argument verbatim with no argfile expansion. + fn push(&mut self, arg: String) { + // Unfortunately, we have to do some eager argparsing to handle unstable + // options which change the behavior of argfile arguments. + // + // Normally, all of the argfile arguments (e.g. `@args.txt`) are + // expanded into our arguments list *and then* the whole list of + // arguments are passed on to be parsed. However, argfile parsing + // options like `-Zshell_argfiles` need to change the behavior of that + // argument expansion. So we have to do a little parsing on our own here + // instead of leaning on the existing logic. + // + // All we care about are unstable options, so we parse those out and + // look for any that affect how we expand argfiles. This argument + // inspection is very conservative; we only change behavior when we see + // exactly the options we're looking for and everything gets passed + // through. + + if self.next_is_unstable_option { + self.inspect_unstable_option(&arg); + self.next_is_unstable_option = false; + } else if let Some(unstable_option) = arg.strip_prefix("-Z") { + if unstable_option.is_empty() { + self.next_is_unstable_option = true; + } else { + self.inspect_unstable_option(unstable_option); + } + } + + self.expanded.push(arg); + } + + /// Consumes the `Expander`, returning the expanded arguments. + fn finish(self) -> Vec { + self.expanded + } + + /// Parses any relevant unstable flags specified on the command line. + fn inspect_unstable_option(&mut self, option: &str) { + match option { + "shell-argfiles" => self.shell_argfiles = true, + _ => (), + } + } + + /// Reads the contents of a file as UTF-8. + fn read_file(path: &str) -> Result { + fs::read_to_string(path).map_err(|e| { + if e.kind() == io::ErrorKind::InvalidData { + Error::Utf8Error(Some(path.to_string())) + } else { + Error::IOError(path.to_string(), e) + } + }) } } @@ -24,20 +98,20 @@ fn arg_expand(arg: String) -> Result, Error> { /// If this function is intended to be used with command line arguments, /// `argv[0]` must be removed prior to calling it manually. pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec { - let mut args = Vec::new(); + let mut expander = Expander::default(); for arg in at_args { - match arg_expand(arg.clone()) { - Ok(arg) => args.extend(arg), - Err(err) => early_dcx.early_fatal(format!("Failed to load argument file: {err}")), + if let Err(err) = expander.arg(arg) { + early_dcx.early_fatal(format!("Failed to load argument file: {err}")); } } - args + expander.finish() } #[derive(Debug)] pub enum Error { Utf8Error(Option), IOError(String, io::Error), + ShellParseError(String), } impl fmt::Display for Error { @@ -46,6 +120,7 @@ impl fmt::Display for Error { Error::Utf8Error(None) => write!(fmt, "Utf8 error"), Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {path}"), Error::IOError(path, err) => write!(fmt, "IO Error: {path}: {err}"), + Error::ShellParseError(path) => write!(fmt, "Invalid shell-style arguments in {path}"), } } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 75410db1e364..7b5de4cc1172 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -700,6 +700,7 @@ fn test_unstable_options_tracking_hash() { untracked!(query_dep_graph, true); untracked!(self_profile, SwitchWithOptPath::Enabled(None)); untracked!(self_profile_events, Some(vec![String::new()])); + untracked!(shell_argfiles, true); untracked!(span_debug, true); untracked!(span_free_formats, true); untracked!(temps_dir, Some(String::from("abc"))); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0b0b67ef890b..21113c298f04 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1875,6 +1875,8 @@ written to standard error output)"), query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"), share_generics: Option = (None, parse_opt_bool, [TRACKED], "make the current crate share its generic instantiations"), + shell_argfiles: bool = (false, parse_bool, [UNTRACKED], + "allow argument files to be specified with POSIX \"shell-style\" argument quoting"), show_span: Option = (None, parse_opt_string, [TRACKED], "show spans for compiler debugging (expr|pat|ty)"), simulate_remapped_rust_src_base: Option = (None, parse_opt_pathbuf, [TRACKED], diff --git a/src/doc/unstable-book/src/compiler-flags/shell-argfiles.md b/src/doc/unstable-book/src/compiler-flags/shell-argfiles.md new file mode 100644 index 000000000000..4f3c780972de --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/shell-argfiles.md @@ -0,0 +1,11 @@ +# `shell-argfiles` + +-------------------- + +The `-Zshell-argfiles` compiler flag allows argfiles to be parsed using POSIX +"shell-style" quoting. When enabled, the compiler will use `shlex` to parse the +arguments from argfiles specified with `@shell:`. + +Because this feature controls the parsing of input arguments, the +`-Zshell-argfiles` flag must be present before the argument specifying the +shell-style arguemnt file. diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 3c00027b9fdc..62d48315d434 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -325,6 +325,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "sha1", "sha2", "sharded-slab", + "shlex", "smallvec", "snap", "stable_deref_trait", diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index b4745d4883c5..ab0e647e1304 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1849; -const ROOT_ENTRY_LIMIT: usize = 867; +const ROOT_ENTRY_LIMIT: usize = 868; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files @@ -36,6 +36,10 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[ "tests/ui/unused-crate-deps/test.mk", // why would you use make "tests/ui/proc-macro/auxiliary/included-file.txt", // more include "tests/ui/invalid/foo.natvis.xml", // sample debugger visualizer + "tests/ui/shell-argfiles/shell-argfiles.args", // passing args via a file + "tests/ui/shell-argfiles/shell-argfiles-badquotes.args", // passing args via a file + "tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args", // passing args via a file + "tests/ui/shell-argfiles/shell-argfiles-via-argfile.args", // passing args via a file ]; fn check_entries(tests_path: &Path, bad: &mut bool) { diff --git a/tests/ui/shell-argfiles/shell-argfiles-badquotes-windows.rs b/tests/ui/shell-argfiles/shell-argfiles-badquotes-windows.rs new file mode 100644 index 000000000000..800735cf3a76 --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles-badquotes-windows.rs @@ -0,0 +1,11 @@ +// Check to see if we can get parameters from an @argsfile file +// +// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path +// separators. This test uses backslash as the path separator for the command +// line arguments and is only run on windows. +// +// only-windows +// compile-flags: --cfg cmdline_set -Z shell-argfiles @shell:{{src-base}}\shell-argfiles\shell-argfiles-badquotes.args + +fn main() { +} diff --git a/tests/ui/shell-argfiles/shell-argfiles-badquotes-windows.stderr b/tests/ui/shell-argfiles/shell-argfiles-badquotes-windows.stderr new file mode 100644 index 000000000000..14adb1f740ab --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles-badquotes-windows.stderr @@ -0,0 +1,2 @@ +error: Failed to load argument file: Invalid shell-style arguments in $DIR/shell-argfiles-badquotes.args + diff --git a/tests/ui/shell-argfiles/shell-argfiles-badquotes.args b/tests/ui/shell-argfiles/shell-argfiles-badquotes.args new file mode 100644 index 000000000000..c0d531adf3ff --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles-badquotes.args @@ -0,0 +1 @@ +"--cfg" "unquoted_set diff --git a/tests/ui/shell-argfiles/shell-argfiles-badquotes.rs b/tests/ui/shell-argfiles/shell-argfiles-badquotes.rs new file mode 100644 index 000000000000..f9160143a041 --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles-badquotes.rs @@ -0,0 +1,12 @@ +// Check to see if we can get parameters from an @argsfile file +// +// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path +// separators. We have a duplicated version of this test that uses backslash as +// the path separator for the command line arguments that is only run on +// windows. +// +// ignore-windows +// compile-flags: --cfg cmdline_set -Z shell-argfiles @shell:{{src-base}}/shell-argfiles/shell-argfiles-badquotes.args + +fn main() { +} diff --git a/tests/ui/shell-argfiles/shell-argfiles-badquotes.stderr b/tests/ui/shell-argfiles/shell-argfiles-badquotes.stderr new file mode 100644 index 000000000000..14adb1f740ab --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles-badquotes.stderr @@ -0,0 +1,2 @@ +error: Failed to load argument file: Invalid shell-style arguments in $DIR/shell-argfiles-badquotes.args + diff --git a/tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args b/tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args new file mode 100644 index 000000000000..4e66d5a03952 --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args @@ -0,0 +1 @@ +"--cfg" "shell_args_set" \ No newline at end of file diff --git a/tests/ui/shell-argfiles/shell-argfiles-via-argfile.args b/tests/ui/shell-argfiles/shell-argfiles-via-argfile.args new file mode 100644 index 000000000000..d0af54e24e33 --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles-via-argfile.args @@ -0,0 +1 @@ +-Zshell-argfiles \ No newline at end of file diff --git a/tests/ui/shell-argfiles/shell-argfiles-via-argfile.rs b/tests/ui/shell-argfiles/shell-argfiles-via-argfile.rs new file mode 100644 index 000000000000..d71e3218f53b --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles-via-argfile.rs @@ -0,0 +1,10 @@ +// Check to see if we can get parameters from an @argsfile file +// +// build-pass +// compile-flags: @{{src-base}}/shell-argfiles/shell-argfiles-via-argfile.args @shell:{{src-base}}/shell-argfiles/shell-argfiles-via-argfile-shell.args + +#[cfg(not(shell_args_set))] +compile_error!("shell_args_set not set"); + +fn main() { +} diff --git a/tests/ui/shell-argfiles/shell-argfiles.args b/tests/ui/shell-argfiles/shell-argfiles.args new file mode 100644 index 000000000000..e5bb4b807ec4 --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles.args @@ -0,0 +1,3 @@ +--cfg unquoted_set +'--cfg' 'single_quoted_set' +"--cfg" "double_quoted_set" diff --git a/tests/ui/shell-argfiles/shell-argfiles.rs b/tests/ui/shell-argfiles/shell-argfiles.rs new file mode 100644 index 000000000000..9bc252ea628a --- /dev/null +++ b/tests/ui/shell-argfiles/shell-argfiles.rs @@ -0,0 +1,19 @@ +// Check to see if we can get parameters from an @argsfile file +// +// build-pass +// compile-flags: --cfg cmdline_set -Z shell-argfiles @shell:{{src-base}}/shell-argfiles/shell-argfiles.args + +#[cfg(not(cmdline_set))] +compile_error!("cmdline_set not set"); + +#[cfg(not(unquoted_set))] +compile_error!("unquoted_set not set"); + +#[cfg(not(single_quoted_set))] +compile_error!("single_quoted_set not set"); + +#[cfg(not(double_quoted_set))] +compile_error!("double_quoted_set not set"); + +fn main() { +} From 755b2da841b7dad218bd34708a592a2bef0f4b21 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 8 Nov 2023 05:56:32 +0000 Subject: [PATCH 230/257] Value recovery can take the whole CycleError --- compiler/rustc_middle/src/query/plumbing.rs | 2 +- compiler/rustc_middle/src/values.rs | 48 +++++++++++++------ compiler/rustc_query_impl/src/lib.rs | 6 +-- .../rustc_query_system/src/query/config.rs | 4 +- compiler/rustc_query_system/src/query/job.rs | 2 +- compiler/rustc_query_system/src/query/mod.rs | 4 +- .../rustc_query_system/src/query/plumbing.rs | 8 ++-- compiler/rustc_query_system/src/values.rs | 11 +++-- 8 files changed, 54 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index d2ff1e3c0944..414d4c8d9496 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -53,7 +53,7 @@ pub struct DynamicQuery<'tcx, C: QueryCache> { fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool, pub hash_result: HashResult, pub value_from_cycle_error: - fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> C::Value, + fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value, pub format_value: fn(&C::Value) -> String, } diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index b3c05a36a138..4ee97dac4444 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::Representability; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_query_system::query::QueryInfo; +use rustc_query_system::query::CycleError; use rustc_query_system::Value; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span}; @@ -14,7 +14,7 @@ use rustc_span::{ErrorGuaranteed, Span}; use std::fmt::Write; impl<'tcx> Value> for Ty<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo], guar: ErrorGuaranteed) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. unsafe { std::mem::transmute::, Ty<'_>>(Ty::new_error(tcx, guar)) } @@ -22,13 +22,13 @@ impl<'tcx> Value> for Ty<'_> { } impl<'tcx> Value> for Result>, CyclePlaceholder> { - fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &[QueryInfo], guar: ErrorGuaranteed) -> Self { + fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { Err(CyclePlaceholder(guar)) } } impl<'tcx> Value> for ty::SymbolName<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo], _guar: ErrorGuaranteed) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, _guar: ErrorGuaranteed) -> Self { // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. unsafe { @@ -40,10 +40,14 @@ impl<'tcx> Value> for ty::SymbolName<'_> { } impl<'tcx> Value> for ty::Binder<'_, ty::FnSig<'_>> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo], guar: ErrorGuaranteed) -> Self { + fn from_cycle_error( + tcx: TyCtxt<'tcx>, + cycle_error: &CycleError, + guar: ErrorGuaranteed, + ) -> Self { let err = Ty::new_error(tcx, guar); - let arity = if let Some(frame) = stack.get(0) + let arity = if let Some(frame) = cycle_error.cycle.get(0) && frame.query.dep_kind == dep_kinds::fn_sig && let Some(def_id) = frame.query.def_id && let Some(node) = tcx.hir().get_if_local(def_id) @@ -70,10 +74,14 @@ impl<'tcx> Value> for ty::Binder<'_, ty::FnSig<'_>> { } impl<'tcx> Value> for Representability { - fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], _guar: ErrorGuaranteed) -> Self { + fn from_cycle_error( + tcx: TyCtxt<'tcx>, + cycle_error: &CycleError, + _guar: ErrorGuaranteed, + ) -> Self { let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); - for info in cycle { + for info in &cycle_error.cycle { if info.query.dep_kind == dep_kinds::representability && let Some(field_id) = info.query.def_id && let Some(field_id) = field_id.as_local() @@ -87,7 +95,7 @@ impl<'tcx> Value> for Representability { item_and_field_ids.push((item_id.expect_local(), field_id)); } } - for info in cycle { + for info in &cycle_error.cycle { if info.query.dep_kind == dep_kinds::representability_adt_ty && let Some(def_id) = info.query.ty_adt_id && let Some(def_id) = def_id.as_local() @@ -102,19 +110,31 @@ impl<'tcx> Value> for Representability { } impl<'tcx> Value> for ty::EarlyBinder> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self { - ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle, guar)) + fn from_cycle_error( + tcx: TyCtxt<'tcx>, + cycle_error: &CycleError, + guar: ErrorGuaranteed, + ) -> Self { + ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle_error, guar)) } } impl<'tcx> Value> for ty::EarlyBinder>> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self { - ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle, guar)) + fn from_cycle_error( + tcx: TyCtxt<'tcx>, + cycle_error: &CycleError, + guar: ErrorGuaranteed, + ) -> Self { + ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle_error, guar)) } } impl<'tcx, T> Value> for Result> { - fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self { + fn from_cycle_error( + _tcx: TyCtxt<'tcx>, + _cycle_error: &CycleError, + guar: ErrorGuaranteed, + ) -> Self { // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really, // tcx.arena.alloc is pretty much equal to leaking). diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index d5883f528199..b5e8ac4018df 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -35,7 +35,7 @@ use rustc_middle::ty::TyCtxt; use rustc_query_system::dep_graph::SerializedDepNodeIndex; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ - get_query_incr, get_query_non_incr, HashResult, QueryCache, QueryConfig, QueryInfo, QueryMap, + get_query_incr, get_query_non_incr, CycleError, HashResult, QueryCache, QueryConfig, QueryMap, QueryMode, QueryState, }; use rustc_query_system::HandleCycleError; @@ -144,10 +144,10 @@ where fn value_from_cycle_error( self, tcx: TyCtxt<'tcx>, - cycle: &[QueryInfo], + cycle_error: &CycleError, guar: ErrorGuaranteed, ) -> Self::Value { - (self.dynamic.value_from_cycle_error)(tcx, cycle, guar) + (self.dynamic.value_from_cycle_error)(tcx, cycle_error, guar) } #[inline(always)] diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index c025fac2631a..958d9fdb52ae 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -5,7 +5,7 @@ use crate::error::HandleCycleError; use crate::ich::StableHashingContext; use crate::query::caches::QueryCache; use crate::query::DepNodeIndex; -use crate::query::{QueryContext, QueryInfo, QueryState}; +use crate::query::{CycleError, QueryContext, QueryState}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_span::ErrorGuaranteed; @@ -57,7 +57,7 @@ pub trait QueryConfig: Copy { fn value_from_cycle_error( self, tcx: Qcx::DepContext, - cycle: &[QueryInfo], + cycle_error: &CycleError, guar: ErrorGuaranteed, ) -> Self::Value; diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 982b9ee94da9..3ef9de7da74b 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -556,7 +556,7 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) { #[inline(never)] #[cold] -pub(crate) fn report_cycle<'a>( +pub fn report_cycle<'a>( sess: &'a Session, CycleError { usage, cycle: stack }: &CycleError, ) -> DiagnosticBuilder<'a> { diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 96a0c7a033aa..ce6a6d6cb1b6 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -4,7 +4,9 @@ pub use self::plumbing::*; mod job; #[cfg(parallel_compiler)] pub use self::job::deadlock; -pub use self::job::{print_query_stack, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap}; +pub use self::job::{ + print_query_stack, report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, +}; mod caches; pub use self::caches::{ diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index c5715d938595..c5a0cc753a8f 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -134,7 +134,7 @@ where match query.handle_cycle_error() { Error => { let guar = error.emit(); - query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar) + query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar) } Fatal => { error.emit(); @@ -143,7 +143,7 @@ where } DelayBug => { let guar = error.delay_as_bug(); - query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar) + query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar) } Stash => { let guar = if let Some(root) = cycle_error.cycle.first() @@ -154,7 +154,7 @@ where } else { error.emit() }; - query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar) + query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar) } } } @@ -211,7 +211,7 @@ where } #[derive(Clone, Debug)] -pub(crate) struct CycleError { +pub struct CycleError { /// The query and related span that uses the cycle. pub usage: Option<(Span, QueryStackFrame)>, pub cycle: Vec, diff --git a/compiler/rustc_query_system/src/values.rs b/compiler/rustc_query_system/src/values.rs index 4f1c182cdb8e..133904f59af1 100644 --- a/compiler/rustc_query_system/src/values.rs +++ b/compiler/rustc_query_system/src/values.rs @@ -1,20 +1,21 @@ use rustc_span::ErrorGuaranteed; use crate::dep_graph::DepContext; -use crate::query::QueryInfo; +use crate::query::CycleError; pub trait Value: Sized { - fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self; + fn from_cycle_error(tcx: Tcx, cycle_error: &CycleError, guar: ErrorGuaranteed) -> Self; } impl Value for T { - default fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo], _guar: ErrorGuaranteed) -> T { + default fn from_cycle_error(tcx: Tcx, cycle_error: &CycleError, _guar: ErrorGuaranteed) -> T { tcx.sess().dcx().abort_if_errors(); // Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's // non-trivial to define it earlier. panic!( - "<{} as Value>::from_cycle_error called without errors: {cycle:#?}", - std::any::type_name::() + "<{} as Value>::from_cycle_error called without errors: {:#?}", + std::any::type_name::(), + cycle_error.cycle, ); } } From dfb9f5df2c9f3c60f2541b3804cb6be1b5c66e63 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 12 Nov 2023 18:57:24 +0000 Subject: [PATCH 231/257] Rustdoc and Clippy stop misusing Key for Ty -> (adt) DefId --- src/librustdoc/html/render/print_item.rs | 3 +-- src/tools/clippy/clippy_lints/src/copies.rs | 3 +-- src/tools/clippy/clippy_lints/src/methods/drain_collect.rs | 7 +++---- .../clippy/clippy_lints/src/methods/redundant_as_str.rs | 7 +------ src/tools/clippy/clippy_lints/src/mut_key.rs | 3 +-- src/tools/clippy/clippy_lints/src/non_copy_const.rs | 3 +-- .../src/transmute/transmute_int_to_non_zero.rs | 5 ++--- 7 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 5ca623f01f1f..3b91fbdcb29d 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -4,7 +4,6 @@ use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; use rustc_index::IndexVec; -use rustc_middle::query::Key; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; @@ -1259,7 +1258,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); - let enum_def_id = ty.ty_adt_id().unwrap(); + let enum_def_id = ty.ty_adt_def().unwrap().did(); wrap_item(w, |w| { let variants_len = variants.len(); diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index d91af76f5e0d..bd07c19a2d81 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -12,7 +12,6 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefIdSet; use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::query::Key; use rustc_session::impl_lint_pass; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; @@ -574,7 +573,7 @@ fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignore let caller_ty = cx.typeck_results().expr_ty(caller_expr); // Check if given type has inner mutability and was not set to ignored by the configuration let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty) - && !matches!(caller_ty.ty_adt_id(), Some(adt_id) if ignored_ty_ids.contains(&adt_id)); + && !matches!(caller_ty.ty_adt_def(), Some(adt) if ignored_ty_ids.contains(&adt.did())); is_inner_mut_ty || caller_ty.is_mutable_ptr() diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs index 6a82d8f756ad..3a8ca37610a9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs @@ -6,7 +6,6 @@ use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; use rustc_lint::LateContext; -use rustc_middle::query::Key; use rustc_middle::ty; use rustc_middle::ty::Ty; use rustc_span::{sym, Symbol}; @@ -18,10 +17,10 @@ use rustc_span::{sym, Symbol}; /// `vec![1,2].drain(..).collect::>()` /// ^^^^^^^^^ ^^^^^^^^^^ false fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool { - if let Some(expr_adt_did) = expr.ty_adt_id() - && let Some(recv_adt_did) = recv.ty_adt_id() + if let Some(expr_adt) = expr.ty_adt_def() + && let Some(recv_adt) = recv.ty_adt_def() { - cx.tcx.is_diagnostic_item(sym, expr_adt_did) && cx.tcx.is_diagnostic_item(sym, recv_adt_did) + cx.tcx.is_diagnostic_item(sym, expr_adt.did()) && cx.tcx.is_diagnostic_item(sym, recv_adt.did()) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs b/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs index 98cd6afc2b79..2a2feedd2b49 100644 --- a/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs +++ b/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs @@ -4,7 +4,6 @@ use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::query::Key; use rustc_span::Span; pub(super) fn check( @@ -14,11 +13,7 @@ pub(super) fn check( as_str_span: Span, other_method_span: Span, ) { - if cx - .tcx - .lang_items() - .string() - .is_some_and(|id| Some(id) == cx.typeck_results().expr_ty(recv).ty_adt_id()) + if cx.typeck_results().expr_ty(recv).ty_adt_def().is_some_and(|adt| Some(adt.did()) == cx.tcx.lang_items().string()) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index 04d2ced6abf8..c32025fcbb6a 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -4,7 +4,6 @@ use clippy_utils::{def_path_def_ids, trait_ref_of_method}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::query::Key; use rustc_middle::ty::{Adt, Ty}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; @@ -166,7 +165,7 @@ impl MutableKeyType { // Determines if a type contains interior mutability which would affect its implementation of // [`Hash`] or [`Ord`]. if is_interior_mut_ty(cx, subst_ty) - && !matches!(subst_ty.ty_adt_id(), Some(adt_id) if self.ignore_mut_def_ids.contains(&adt_id)) + && !matches!(subst_ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did())) { span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); } diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 4013cb34561a..f8365deebd46 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -15,7 +15,6 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; -use rustc_middle::query::Key; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; @@ -188,7 +187,7 @@ impl NonCopyConst { } fn is_ty_ignored(&self, ty: Ty<'_>) -> bool { - matches!(ty.ty_adt_id(), Some(adt_id) if self.ignore_mut_def_ids.contains(&adt_id)) + matches!(ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did())) } fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index c0d0d2b93dc0..5df645491ff8 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -4,7 +4,6 @@ use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::query::Key; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::sym; @@ -17,10 +16,10 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, ) -> bool { - let (ty::Int(_) | ty::Uint(_), Some(to_ty_id)) = (&from_ty.kind(), to_ty.ty_adt_id()) else { + let (ty::Int(_) | ty::Uint(_), Some(to_ty_adt)) = (&from_ty.kind(), to_ty.ty_adt_def()) else { return false; }; - let Some(to_type_sym) = cx.tcx.get_diagnostic_name(to_ty_id) else { + let Some(to_type_sym) = cx.tcx.get_diagnostic_name(to_ty_adt.did()) else { return false; }; From 82a22154815b70e5701064bef59d3334f5bc1cf7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 8 Nov 2023 06:56:06 +0000 Subject: [PATCH 232/257] Don't check for recursion in generator witness fields --- .../src/error_codes/E0733.md | 16 ++++++++- compiler/rustc_hir/src/hir.rs | 6 ++++ .../rustc_hir_analysis/src/check/check.rs | 25 ++++--------- compiler/rustc_middle/src/query/keys.rs | 13 ++++--- compiler/rustc_middle/src/query/mod.rs | 2 ++ compiler/rustc_middle/src/ty/util.rs | 13 +------ compiler/rustc_middle/src/values.rs | 36 ++++++++++++++++--- compiler/rustc_query_impl/src/plumbing.rs | 4 +-- compiler/rustc_query_system/src/query/mod.rs | 7 ++-- .../src/traits/query/normalize.rs | 13 +++---- ...utually-recursive-async-impl-trait-type.rs | 2 +- ...lly-recursive-async-impl-trait-type.stderr | 11 +----- .../recursive-coroutine.current.stderr | 12 ------- .../recursive-coroutine.next.stderr | 12 ------- tests/ui/impl-trait/recursive-coroutine.rs | 5 +-- .../recursive-impl-trait-type-indirect.rs | 2 +- .../recursive-impl-trait-type-indirect.stderr | 17 ++++----- .../indirect-recursion-issue-112047.rs | 3 +- .../indirect-recursion-issue-112047.stderr | 27 ++++---------- 19 files changed, 102 insertions(+), 124 deletions(-) delete mode 100644 tests/ui/impl-trait/recursive-coroutine.current.stderr delete mode 100644 tests/ui/impl-trait/recursive-coroutine.next.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0733.md b/compiler/rustc_error_codes/src/error_codes/E0733.md index 051b75148e50..cceb0880350e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0733.md +++ b/compiler/rustc_error_codes/src/error_codes/E0733.md @@ -13,7 +13,7 @@ async fn foo(n: usize) { To perform async recursion, the `async fn` needs to be desugared such that the `Future` is explicit in the return type: -```edition2018,compile_fail,E0720 +```edition2018,compile_fail,E0733 use std::future::Future; fn foo_desugared(n: usize) -> impl Future { async move { @@ -41,4 +41,18 @@ fn foo_recursive(n: usize) -> Pin>> { The `Box<...>` ensures that the result is of known size, and the pin is required to keep it in the same place in memory. +Alternatively, the recursive call-site can be boxed: + +```edition2018 +use std::future::Future; +use std::pin::Pin; +fn foo_recursive(n: usize) -> impl Future { + async move { + if n > 0 { + Box::pin(foo_recursive(n - 1)).await; + } + } +} +``` + [`async`]: https://doc.rust-lang.org/std/keyword.async.html diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e88b876534e5..cadf54c76a3f 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1361,6 +1361,12 @@ impl CoroutineKind { } } +impl CoroutineKind { + pub fn is_fn_like(self) -> bool { + matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn)) + } +} + impl fmt::Display for CoroutineKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 4b26a469eb56..12430b1465c3 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -213,13 +213,12 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) { return; } - let args = GenericArgs::identity_for_item(tcx, item.owner_id); let span = tcx.def_span(item.owner_id.def_id); if tcx.type_of(item.owner_id.def_id).instantiate_identity().references_error() { return; } - if check_opaque_for_cycles(tcx, item.owner_id.def_id, args, span, origin).is_err() { + if check_opaque_for_cycles(tcx, item.owner_id.def_id, span).is_err() { return; } @@ -230,16 +229,16 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) { pub(super) fn check_opaque_for_cycles<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, - args: GenericArgsRef<'tcx>, span: Span, - origin: &hir::OpaqueTyOrigin, ) -> Result<(), ErrorGuaranteed> { + let args = GenericArgs::identity_for_item(tcx, def_id); if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() { - let reported = match origin { - hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span), - _ => opaque_type_cycle_error(tcx, def_id, span), - }; + let reported = opaque_type_cycle_error(tcx, def_id, span); Err(reported) + } else if let Err(&LayoutError::Cycle(guar)) = + tcx.layout_of(tcx.param_env(def_id).and(Ty::new_opaque(tcx, def_id.to_def_id(), args))) + { + Err(guar) } else { Ok(()) } @@ -1300,16 +1299,6 @@ pub(super) fn check_type_params_are_used<'tcx>( } } -fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed { - struct_span_err!(tcx.dcx(), span, E0733, "recursion in an `async fn` requires boxing") - .span_label_mv(span, "recursive `async fn`") - .note_mv("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") - .note_mv( - "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion", - ) - .emit() -} - /// Emit an error for recursive opaque types. /// /// If this is a return `impl Trait`, find the item's return expressions and point at them. For diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 113763450529..945f17d5df2a 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -40,7 +40,7 @@ pub trait Key: Sized { None } - fn ty_adt_id(&self) -> Option { + fn ty_def_id(&self) -> Option { None } } @@ -406,9 +406,10 @@ impl<'tcx> Key for Ty<'tcx> { DUMMY_SP } - fn ty_adt_id(&self) -> Option { - match self.kind() { + fn ty_def_id(&self) -> Option { + match *self.kind() { ty::Adt(adt, _) => Some(adt.did()), + ty::Coroutine(def_id, ..) => Some(def_id), _ => None, } } @@ -452,6 +453,10 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.value.default_span(tcx) } + + fn ty_def_id(&self) -> Option { + self.value.ty_def_id() + } } impl Key for Symbol { @@ -550,7 +555,7 @@ impl<'tcx> Key for (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>) { DUMMY_SP } - fn ty_adt_id(&self) -> Option { + fn ty_def_id(&self) -> Option { match self.1.value.kind() { ty::Adt(adt, _) => Some(adt.did()), _ => None, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 7d5abaceb20a..0e3b9984423f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1387,6 +1387,8 @@ rustc_queries! { ) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { depth_limit desc { "computing layout of `{}`", key.value } + // we emit our own error during query cycle handling + cycle_delay_bug } /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index b9c75bd205b2..74dba41647bd 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -896,18 +896,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> { } let args = args.fold_with(self); if !self.check_recursion || self.seen_opaque_tys.insert(def_id) { - let expanded_ty = match self.expanded_cache.get(&(def_id, args)) { - Some(expanded_ty) => *expanded_ty, - None => { - for bty in self.tcx.coroutine_hidden_types(def_id) { - let hidden_ty = bty.instantiate(self.tcx, args); - self.fold_ty(hidden_ty); - } - let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args); - self.expanded_cache.insert((def_id, args), expanded_ty); - expanded_ty - } - }; + let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args); if self.check_recursion { self.seen_opaque_tys.remove(&def_id); } diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 4ee97dac4444..0179829cc46a 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::Representability; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_query_system::query::CycleError; +use rustc_query_system::query::{report_cycle, CycleError}; use rustc_query_system::Value; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span}; @@ -97,7 +97,7 @@ impl<'tcx> Value> for Representability { } for info in &cycle_error.cycle { if info.query.dep_kind == dep_kinds::representability_adt_ty - && let Some(def_id) = info.query.ty_adt_id + && let Some(def_id) = info.query.ty_def_id && let Some(def_id) = def_id.as_local() && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) { @@ -131,10 +131,36 @@ impl<'tcx> Value> for ty::EarlyBinder> impl<'tcx, T> Value> for Result> { fn from_cycle_error( - _tcx: TyCtxt<'tcx>, - _cycle_error: &CycleError, - guar: ErrorGuaranteed, + tcx: TyCtxt<'tcx>, + cycle_error: &CycleError, + _guar: ErrorGuaranteed, ) -> Self { + let guar = if cycle_error.cycle[0].query.dep_kind == dep_kinds::layout_of + && let Some(def_id) = cycle_error.cycle[0].query.ty_def_id + && let Some(def_id) = def_id.as_local() + && matches!(tcx.def_kind(def_id), DefKind::Closure) + && let Some(coroutine_kind) = tcx.coroutine_kind(def_id) + { + // FIXME: `def_span` for an fn-like coroutine will point to the fn's body + // due to interactions between the desugaring into a closure expr and the + // def_span code. I'm not motivated to fix it, because I tried and it was + // not working, so just hack around it by grabbing the parent fn's span. + let span = if coroutine_kind.is_fn_like() { + tcx.def_span(tcx.local_parent(def_id)) + } else { + tcx.def_span(def_id) + }; + struct_span_err!(tcx.sess.dcx(), span, E0733, "recursion in an `async fn` requires boxing") + .span_label(span, "recursive `async fn`") + .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") + .note( + "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion", + ) + .emit() + } else { + report_cycle(tcx.sess, cycle_error).emit() + }; + // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really, // tcx.arena.alloc is pretty much equal to leaking). diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index f131a0f75932..7e0fbf3d76cb 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -342,9 +342,9 @@ pub(crate) fn create_query_frame< hasher.finish::() }) }; - let ty_adt_id = key.ty_adt_id(); + let ty_def_id = key.ty_def_id(); - QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_adt_id, hash) + QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_def_id, hash) } pub(crate) fn encode_query_results<'a, 'tcx, Q>( diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index ce6a6d6cb1b6..9ff04c4e910d 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -35,7 +35,8 @@ pub struct QueryStackFrame { span: Option, pub def_id: Option, pub def_kind: Option, - pub ty_adt_id: Option, + /// A def-id that is extracted from a `Ty` in a query key + pub ty_def_id: Option, pub dep_kind: DepKind, /// This hash is used to deterministically pick /// a query to remove cycles in the parallel compiler. @@ -51,7 +52,7 @@ impl QueryStackFrame { def_id: Option, def_kind: Option, dep_kind: DepKind, - ty_adt_id: Option, + ty_def_id: Option, _hash: impl FnOnce() -> Hash64, ) -> Self { Self { @@ -59,7 +60,7 @@ impl QueryStackFrame { span, def_id, def_kind, - ty_adt_id, + ty_def_id, dep_kind, #[cfg(parallel_compiler)] hash: _hash(), diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index e8867187a408..425c4fbe9c50 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -239,16 +239,13 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> } let generic_ty = self.interner().type_of(data.def_id); - let concrete_ty = generic_ty.instantiate(self.interner(), args); + let mut concrete_ty = generic_ty.instantiate(self.interner(), args); self.anon_depth += 1; if concrete_ty == ty { - bug!( - "infinite recursion generic_ty: {:#?}, args: {:#?}, \ - concrete_ty: {:#?}, ty: {:#?}", - generic_ty, - args, - concrete_ty, - ty + concrete_ty = Ty::new_error_with_message( + self.interner(), + DUMMY_SP, + "recursive opaque type", ); } let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty)); diff --git a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs index bb2a61f03ce1..d38ba1a569bb 100644 --- a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs +++ b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs @@ -6,7 +6,7 @@ async fn rec_1() { //~ ERROR recursion in an `async fn` rec_2().await; } -async fn rec_2() { //~ ERROR recursion in an `async fn` +async fn rec_2() { rec_1().await; } diff --git a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr index 9442609e8058..dd53075be602 100644 --- a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr +++ b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr @@ -7,15 +7,6 @@ LL | async fn rec_1() { = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion -error[E0733]: recursion in an `async fn` requires boxing - --> $DIR/mutually-recursive-async-impl-trait-type.rs:9:1 - | -LL | async fn rec_2() { - | ^^^^^^^^^^^^^^^^ recursive `async fn` - | - = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` - = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0733`. diff --git a/tests/ui/impl-trait/recursive-coroutine.current.stderr b/tests/ui/impl-trait/recursive-coroutine.current.stderr deleted file mode 100644 index e838634ed087..000000000000 --- a/tests/ui/impl-trait/recursive-coroutine.current.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0720]: cannot resolve opaque type - --> $DIR/recursive-coroutine.rs:7:13 - | -LL | fn foo() -> impl Coroutine { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type -... -LL | let mut gen = Box::pin(foo()); - | ------- coroutine captures itself here - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/recursive-coroutine.next.stderr b/tests/ui/impl-trait/recursive-coroutine.next.stderr deleted file mode 100644 index e838634ed087..000000000000 --- a/tests/ui/impl-trait/recursive-coroutine.next.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0720]: cannot resolve opaque type - --> $DIR/recursive-coroutine.rs:7:13 - | -LL | fn foo() -> impl Coroutine { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type -... -LL | let mut gen = Box::pin(foo()); - | ------- coroutine captures itself here - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/recursive-coroutine.rs b/tests/ui/impl-trait/recursive-coroutine.rs index b82fe134a406..b9291f07e213 100644 --- a/tests/ui/impl-trait/recursive-coroutine.rs +++ b/tests/ui/impl-trait/recursive-coroutine.rs @@ -1,3 +1,4 @@ +// check-pass // revisions: current next //[next] compile-flags: -Znext-solver #![feature(coroutines, coroutine_trait)] @@ -5,12 +6,8 @@ use std::ops::{Coroutine, CoroutineState}; fn foo() -> impl Coroutine { - //~^ ERROR cannot resolve opaque type - //~| NOTE recursive opaque type - //~| NOTE in this expansion of desugaring of || { let mut gen = Box::pin(foo()); - //~^ NOTE coroutine captures itself here let mut r = gen.as_mut().resume(()); while let CoroutineState::Yielded(v) = r { yield v; diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs index 8331eec906e1..a6bca107b1ec 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs @@ -70,8 +70,8 @@ fn substs_change() -> impl Sized { } fn coroutine_hold() -> impl Sized { - //~^ ERROR move || { + //~^ ERROR let x = coroutine_hold(); yield; x; diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 8e9aa8ad0a69..0dabba264689 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -109,14 +109,14 @@ LL | LL | (substs_change::<&T>(),) | ------------------------ returning here with type `(impl Sized,)` -error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:72:24 +error[E0733]: recursion in an `async fn` requires boxing + --> $DIR/recursive-impl-trait-type-indirect.rs:73:5 | -LL | fn coroutine_hold() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | let x = coroutine_hold(); - | - coroutine captures itself here +LL | move || { + | ^^^^^^^ recursive `async fn` + | + = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` + = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:86:26 @@ -144,4 +144,5 @@ LL | mutual_recursion() error: aborting due to 14 previous errors -For more information about this error, try `rustc --explain E0720`. +Some errors have detailed explanations: E0720, E0733. +For more information about an error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs index 6a2ee761e191..3637f416c7b5 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs @@ -1,5 +1,4 @@ // edition: 2021 -// build-fail #![feature(impl_trait_in_assoc_type)] @@ -20,7 +19,7 @@ impl Recur for () { fn recur(self) -> Self::Recur { async move { recur(self).await; } - //~^ ERROR cycle detected when computing layout of + //~^ ERROR recursion in an `async fn` requires boxing } } diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr index 11d9cd0af081..aa352b326c64 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr @@ -1,27 +1,12 @@ -error[E0391]: cycle detected when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}` - --> $DIR/indirect-recursion-issue-112047.rs:22:22 +error[E0733]: recursion in an `async fn` requires boxing + --> $DIR/indirect-recursion-issue-112047.rs:21:9 | LL | async move { recur(self).await; } - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn` | - = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}>`... - = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}>`... -note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}`... - --> $DIR/indirect-recursion-issue-112047.rs:15:5 - | -LL | t.recur().await; - | ^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<() as Recur>::Recur>`... - = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}>`... - = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}>`... - = note: ...which again requires computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}`, completing the cycle -note: cycle used when elaborating drops for `::recur` - --> $DIR/indirect-recursion-issue-112047.rs:21:5 - | -LL | fn recur(self) -> Self::Recur { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = 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 + = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` + = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0391`. +For more information about this error, try `rustc --explain E0733`. From 199af7cef0cd31e201307769d974631847cf98fd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 8 Nov 2023 07:19:01 +0000 Subject: [PATCH 233/257] Point out source of recursion --- compiler/rustc_middle/src/ty/util.rs | 45 +++++++++++--- compiler/rustc_middle/src/values.rs | 58 ++++++++++++++++--- .../in-trait/async-recursive-generic.rs | 2 +- .../in-trait/async-recursive-generic.stderr | 9 ++- .../async-await/in-trait/async-recursive.rs | 2 +- .../in-trait/async-recursive.stderr | 9 ++- ...utually-recursive-async-impl-trait-type.rs | 2 +- ...lly-recursive-async-impl-trait-type.stderr | 15 ++++- .../recursive-async-impl-trait-type.rs | 2 +- .../recursive-async-impl-trait-type.stderr | 9 ++- .../recursive-impl-trait-type-indirect.stderr | 10 ++-- .../indirect-recursion-issue-112047.rs | 2 +- .../indirect-recursion-issue-112047.stderr | 15 ++++- 13 files changed, 139 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 74dba41647bd..f11a24543f3c 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -729,16 +729,43 @@ impl<'tcx> TyCtxt<'tcx> { DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "method", DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => { match coroutine_kind { - hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => { - "async closure" - } - hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => { - "async gen closure" - } + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Fn, + ) => "async fn", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Block, + ) => "async block", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Closure, + ) => "async closure", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::AsyncGen, + hir::CoroutineSource::Fn, + ) => "async gen fn", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::AsyncGen, + hir::CoroutineSource::Block, + ) => "async gen block", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::AsyncGen, + hir::CoroutineSource::Closure, + ) => "async gen closure", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Gen, + hir::CoroutineSource::Fn, + ) => "gen fn", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Gen, + hir::CoroutineSource::Block, + ) => "gen block", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Gen, + hir::CoroutineSource::Closure, + ) => "gen closure", hir::CoroutineKind::Coroutine(_) => "coroutine", - hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => { - "gen closure" - } } } _ => def_kind.descr(def_id), diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 0179829cc46a..3e3aa821b4ec 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -138,7 +138,8 @@ impl<'tcx, T> Value> for Result> let guar = if cycle_error.cycle[0].query.dep_kind == dep_kinds::layout_of && let Some(def_id) = cycle_error.cycle[0].query.ty_def_id && let Some(def_id) = def_id.as_local() - && matches!(tcx.def_kind(def_id), DefKind::Closure) + && let def_kind = tcx.def_kind(def_id) + && matches!(def_kind, DefKind::Closure) && let Some(coroutine_kind) = tcx.coroutine_kind(def_id) { // FIXME: `def_span` for an fn-like coroutine will point to the fn's body @@ -150,13 +151,56 @@ impl<'tcx, T> Value> for Result> } else { tcx.def_span(def_id) }; - struct_span_err!(tcx.sess.dcx(), span, E0733, "recursion in an `async fn` requires boxing") - .span_label(span, "recursive `async fn`") - .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") - .note( + let mut diag = struct_span_err!( + tcx.sess.dcx(), + span, + E0733, + "recursion in {} {} requires boxing", + tcx.def_kind_descr_article(def_kind, def_id.to_def_id()), + tcx.def_kind_descr(def_kind, def_id.to_def_id()), + ); + for (i, frame) in cycle_error.cycle.iter().enumerate() { + if frame.query.dep_kind != dep_kinds::layout_of { + continue; + } + let Some(frame_def_id) = frame.query.ty_def_id else { + continue; + }; + let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else { + continue; + }; + let frame_span = frame + .query + .default_span(cycle_error.cycle[(i + 1) % cycle_error.cycle.len()].span); + if frame_span.is_dummy() { + continue; + } + if i == 0 { + diag.span_label(frame_span, "recursive call here"); + } else { + let coroutine_span = if frame_coroutine_kind.is_fn_like() { + tcx.def_span(tcx.parent(frame_def_id)) + } else { + tcx.def_span(frame_def_id) + }; + let mut multispan = MultiSpan::from_span(coroutine_span); + multispan.push_span_label(frame_span, "...leading to this recursive call"); + diag.span_note( + multispan, + format!("which leads to this {}", tcx.def_descr(frame_def_id)), + ); + } + } + if matches!( + coroutine_kind, + hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) + ) { + diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future"); + diag.note( "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion", - ) - .emit() + ); + } + diag.emit() } else { report_cycle(tcx.sess, cycle_error).emit() }; diff --git a/tests/ui/async-await/in-trait/async-recursive-generic.rs b/tests/ui/async-await/in-trait/async-recursive-generic.rs index c6031ce28d1d..33eb2b2de131 100644 --- a/tests/ui/async-await/in-trait/async-recursive-generic.rs +++ b/tests/ui/async-await/in-trait/async-recursive-generic.rs @@ -6,7 +6,7 @@ trait MyTrait { impl MyTrait for T where T: Copy { async fn foo_recursive(&self, n: usize) -> T { - //~^ ERROR recursion in an `async fn` requires boxing + //~^ ERROR recursion in an async fn requires boxing if n > 0 { self.foo_recursive(n - 1).await } else { diff --git a/tests/ui/async-await/in-trait/async-recursive-generic.stderr b/tests/ui/async-await/in-trait/async-recursive-generic.stderr index 11489c18ad40..37de274565d3 100644 --- a/tests/ui/async-await/in-trait/async-recursive-generic.stderr +++ b/tests/ui/async-await/in-trait/async-recursive-generic.stderr @@ -1,10 +1,13 @@ -error[E0733]: recursion in an `async fn` requires boxing +error[E0733]: recursion in an async fn requires boxing --> $DIR/async-recursive-generic.rs:8:5 | LL | async fn foo_recursive(&self, n: usize) -> T { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | self.foo_recursive(n - 1).await + | ------------------------------- recursive call here | - = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` + = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error diff --git a/tests/ui/async-await/in-trait/async-recursive.rs b/tests/ui/async-await/in-trait/async-recursive.rs index 09f1ffe499e3..2534c43413ed 100644 --- a/tests/ui/async-await/in-trait/async-recursive.rs +++ b/tests/ui/async-await/in-trait/async-recursive.rs @@ -6,7 +6,7 @@ trait MyTrait { impl MyTrait for i32 { async fn foo_recursive(&self, n: usize) -> i32 { - //~^ ERROR recursion in an `async fn` requires boxing + //~^ ERROR recursion in an async fn requires boxing if n > 0 { self.foo_recursive(n - 1).await } else { diff --git a/tests/ui/async-await/in-trait/async-recursive.stderr b/tests/ui/async-await/in-trait/async-recursive.stderr index 587962857269..6b99c516c3b5 100644 --- a/tests/ui/async-await/in-trait/async-recursive.stderr +++ b/tests/ui/async-await/in-trait/async-recursive.stderr @@ -1,10 +1,13 @@ -error[E0733]: recursion in an `async fn` requires boxing +error[E0733]: recursion in an async fn requires boxing --> $DIR/async-recursive.rs:8:5 | LL | async fn foo_recursive(&self, n: usize) -> i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | self.foo_recursive(n - 1).await + | ------------------------------- recursive call here | - = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` + = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error diff --git a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs index d38ba1a569bb..fedc814b0418 100644 --- a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs +++ b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs @@ -2,7 +2,7 @@ // Test that impl trait does not allow creating recursive types that are // otherwise forbidden when using `async` and `await`. -async fn rec_1() { //~ ERROR recursion in an `async fn` +async fn rec_1() { //~ ERROR recursion in an async fn rec_2().await; } diff --git a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr index dd53075be602..3f2ee4150dae 100644 --- a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr +++ b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr @@ -1,10 +1,19 @@ -error[E0733]: recursion in an `async fn` requires boxing +error[E0733]: recursion in an async fn requires boxing --> $DIR/mutually-recursive-async-impl-trait-type.rs:5:1 | LL | async fn rec_1() { - | ^^^^^^^^^^^^^^^^ recursive `async fn` + | ^^^^^^^^^^^^^^^^ +LL | rec_2().await; + | ------------- recursive call here | - = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` +note: which leads to this async fn + --> $DIR/mutually-recursive-async-impl-trait-type.rs:9:1 + | +LL | async fn rec_2() { + | ^^^^^^^^^^^^^^^^ +LL | rec_1().await; + | ------------- ...leading to this recursive call + = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error diff --git a/tests/ui/async-await/recursive-async-impl-trait-type.rs b/tests/ui/async-await/recursive-async-impl-trait-type.rs index edc4cb8ac5df..9351ee53f075 100644 --- a/tests/ui/async-await/recursive-async-impl-trait-type.rs +++ b/tests/ui/async-await/recursive-async-impl-trait-type.rs @@ -3,7 +3,7 @@ // otherwise forbidden when using `async` and `await`. async fn recursive_async_function() -> () { - //~^ ERROR recursion in an `async fn` requires boxing + //~^ ERROR recursion in an async fn requires boxing recursive_async_function().await; } diff --git a/tests/ui/async-await/recursive-async-impl-trait-type.stderr b/tests/ui/async-await/recursive-async-impl-trait-type.stderr index 969258f84ed6..cec92c54f01f 100644 --- a/tests/ui/async-await/recursive-async-impl-trait-type.stderr +++ b/tests/ui/async-await/recursive-async-impl-trait-type.stderr @@ -1,10 +1,13 @@ -error[E0733]: recursion in an `async fn` requires boxing +error[E0733]: recursion in an async fn requires boxing --> $DIR/recursive-async-impl-trait-type.rs:5:1 | LL | async fn recursive_async_function() -> () { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | recursive_async_function().await; + | -------------------------------- recursive call here | - = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` + = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 0dabba264689..16a9012958e9 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -109,14 +109,14 @@ LL | LL | (substs_change::<&T>(),) | ------------------------ returning here with type `(impl Sized,)` -error[E0733]: recursion in an `async fn` requires boxing +error[E0733]: recursion in a coroutine requires boxing --> $DIR/recursive-impl-trait-type-indirect.rs:73:5 | LL | move || { - | ^^^^^^^ recursive `async fn` - | - = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` - = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion + | ^^^^^^^ +LL | +LL | let x = coroutine_hold(); + | - recursive call here error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:86:26 diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs index 3637f416c7b5..7a2ea881b643 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs @@ -19,7 +19,7 @@ impl Recur for () { fn recur(self) -> Self::Recur { async move { recur(self).await; } - //~^ ERROR recursion in an `async fn` requires boxing + //~^ ERROR recursion in an async block requires boxing } } diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr index aa352b326c64..5427ebe92ad9 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr @@ -1,10 +1,19 @@ -error[E0733]: recursion in an `async fn` requires boxing +error[E0733]: recursion in an async block requires boxing --> $DIR/indirect-recursion-issue-112047.rs:21:9 | LL | async move { recur(self).await; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn` + | ^^^^^^^^^^^^^-----------------^^^ + | | + | recursive call here | - = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` +note: which leads to this async fn + --> $DIR/indirect-recursion-issue-112047.rs:13:1 + | +LL | async fn recur(t: impl Recur) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | t.recur().await; + | --------------- ...leading to this recursive call + = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error From fa2ff51ace4f1c415cbe24687828058bcf74d200 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 13 Nov 2023 02:08:14 +0000 Subject: [PATCH 234/257] Only compute layout of opaque if coroutine is the cause of an opaque cycle --- .../rustc_hir_analysis/src/check/check.rs | 35 ++++++++++++++----- compiler/rustc_middle/src/ty/util.rs | 25 ++++++++++++- .../indirect-recursion-issue-112047.rs | 1 + .../indirect-recursion-issue-112047.stderr | 4 +-- 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 12430b1465c3..7f23c04ce2df 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -17,7 +17,7 @@ use rustc_middle::middle::stability::EvalResult; use rustc_middle::traits::{DefiningAnchor, ObligationCauseCode}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; -use rustc_middle::ty::util::{Discr, IntTypeExt}; +use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt}; use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::{ AdtDef, ParamEnv, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, @@ -232,16 +232,33 @@ pub(super) fn check_opaque_for_cycles<'tcx>( span: Span, ) -> Result<(), ErrorGuaranteed> { let args = GenericArgs::identity_for_item(tcx, def_id); - if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() { - let reported = opaque_type_cycle_error(tcx, def_id, span); - Err(reported) - } else if let Err(&LayoutError::Cycle(guar)) = - tcx.layout_of(tcx.param_env(def_id).and(Ty::new_opaque(tcx, def_id.to_def_id(), args))) + + // First, try to look at any opaque expansion cycles, considering coroutine fields + // (even though these aren't necessarily true errors). + if tcx + .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::Yes) + .is_err() { - Err(guar) - } else { - Ok(()) + // Look for true opaque expansion cycles, but ignore coroutines. + // This will give us any true errors. Coroutines are only problematic + // if they cause layout computation errors. + if tcx + .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::No) + .is_err() + { + let reported = opaque_type_cycle_error(tcx, def_id, span); + return Err(reported); + } + + // And also look for cycle errors in the layout of coroutines. + if let Err(&LayoutError::Cycle(guar)) = + tcx.layout_of(tcx.param_env(def_id).and(Ty::new_opaque(tcx, def_id.to_def_id(), args))) + { + return Err(guar); + } } + + Ok(()) } /// Check that the concrete type behind `impl Trait` actually implements `Trait`. diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index f11a24543f3c..d9f2034bba53 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -702,6 +702,7 @@ impl<'tcx> TyCtxt<'tcx> { self, def_id: DefId, args: GenericArgsRef<'tcx>, + inspect_coroutine_fields: InspectCoroutineFields, ) -> Result, Ty<'tcx>> { let mut visitor = OpaqueTypeExpander { seen_opaque_tys: FxHashSet::default(), @@ -712,6 +713,7 @@ impl<'tcx> TyCtxt<'tcx> { check_recursion: true, expand_coroutines: true, tcx: self, + inspect_coroutine_fields, }; let expanded_type = visitor.expand_opaque_ty(def_id, args).unwrap(); @@ -885,6 +887,13 @@ struct OpaqueTypeExpander<'tcx> { /// recursion, and 'false' otherwise to avoid unnecessary work. check_recursion: bool, tcx: TyCtxt<'tcx>, + inspect_coroutine_fields: InspectCoroutineFields, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum InspectCoroutineFields { + No, + Yes, } impl<'tcx> OpaqueTypeExpander<'tcx> { @@ -923,7 +932,20 @@ impl<'tcx> OpaqueTypeExpander<'tcx> { } let args = args.fold_with(self); if !self.check_recursion || self.seen_opaque_tys.insert(def_id) { - let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args); + let expanded_ty = match self.expanded_cache.get(&(def_id, args)) { + Some(expanded_ty) => *expanded_ty, + None => { + if matches!(self.inspect_coroutine_fields, InspectCoroutineFields::Yes) { + for bty in self.tcx.coroutine_hidden_types(def_id) { + let hidden_ty = bty.instantiate(self.tcx, args); + self.fold_ty(hidden_ty); + } + } + let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args); + self.expanded_cache.insert((def_id, args), expanded_ty); + expanded_ty + } + }; if self.check_recursion { self.seen_opaque_tys.remove(&def_id); } @@ -1495,6 +1517,7 @@ pub fn reveal_opaque_types_in_bounds<'tcx>( check_recursion: false, expand_coroutines: false, tcx, + inspect_coroutine_fields: InspectCoroutineFields::No, }; val.fold_with(&mut visitor) } diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs index 7a2ea881b643..e7b23d5f8a1c 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs @@ -1,4 +1,5 @@ // edition: 2021 +// build-fail #![feature(impl_trait_in_assoc_type)] diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr index 5427ebe92ad9..2e75f676971c 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr @@ -1,5 +1,5 @@ error[E0733]: recursion in an async block requires boxing - --> $DIR/indirect-recursion-issue-112047.rs:21:9 + --> $DIR/indirect-recursion-issue-112047.rs:22:9 | LL | async move { recur(self).await; } | ^^^^^^^^^^^^^-----------------^^^ @@ -7,7 +7,7 @@ LL | async move { recur(self).await; } | recursive call here | note: which leads to this async fn - --> $DIR/indirect-recursion-issue-112047.rs:13:1 + --> $DIR/indirect-recursion-issue-112047.rs:14:1 | LL | async fn recur(t: impl Recur) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 841184bcae3000335c26bfae24f8be44beb81759 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 24 Nov 2023 15:05:11 +0000 Subject: [PATCH 235/257] Make cycle error more resilient to where it starts Also don't recomment recursive_async crate anymore Co-authored-by: lcnr --- compiler/rustc_middle/src/values.rs | 131 +++++++++--------- .../in-trait/async-recursive-generic.stderr | 1 - .../in-trait/async-recursive.stderr | 1 - .../indirect-recursion-issue-112047.rs | 4 +- .../indirect-recursion-issue-112047.stderr | 15 +- ...lly-recursive-async-impl-trait-type.stderr | 1 - .../recursive-async-impl-trait-type.stderr | 1 - ...outine.rs => recursive-coroutine-boxed.rs} | 0 ...ecursive-coroutine-indirect.current.stderr | 11 ++ .../recursive-coroutine-indirect.next.stderr | 11 ++ .../recursive-coroutine-indirect.rs | 13 ++ .../recursive-impl-trait-type-indirect.rs | 10 -- .../recursive-impl-trait-type-indirect.stderr | 50 +++---- .../indirect-recursion-issue-112047.stderr | 1 - 14 files changed, 134 insertions(+), 116 deletions(-) rename tests/ui/impl-trait/{recursive-coroutine.rs => recursive-coroutine-boxed.rs} (100%) create mode 100644 tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr create mode 100644 tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr create mode 100644 tests/ui/impl-trait/recursive-coroutine-indirect.rs diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 3e3aa821b4ec..77e4ed06eaa2 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -11,6 +11,7 @@ use rustc_query_system::Value; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span}; +use std::collections::VecDeque; use std::fmt::Write; impl<'tcx> Value> for Ty<'_> { @@ -135,73 +136,79 @@ impl<'tcx, T> Value> for Result> cycle_error: &CycleError, _guar: ErrorGuaranteed, ) -> Self { - let guar = if cycle_error.cycle[0].query.dep_kind == dep_kinds::layout_of - && let Some(def_id) = cycle_error.cycle[0].query.ty_def_id - && let Some(def_id) = def_id.as_local() - && let def_kind = tcx.def_kind(def_id) - && matches!(def_kind, DefKind::Closure) - && let Some(coroutine_kind) = tcx.coroutine_kind(def_id) - { - // FIXME: `def_span` for an fn-like coroutine will point to the fn's body - // due to interactions between the desugaring into a closure expr and the - // def_span code. I'm not motivated to fix it, because I tried and it was - // not working, so just hack around it by grabbing the parent fn's span. - let span = if coroutine_kind.is_fn_like() { - tcx.def_span(tcx.local_parent(def_id)) - } else { - tcx.def_span(def_id) - }; - let mut diag = struct_span_err!( - tcx.sess.dcx(), - span, - E0733, - "recursion in {} {} requires boxing", - tcx.def_kind_descr_article(def_kind, def_id.to_def_id()), - tcx.def_kind_descr(def_kind, def_id.to_def_id()), - ); - for (i, frame) in cycle_error.cycle.iter().enumerate() { - if frame.query.dep_kind != dep_kinds::layout_of { - continue; - } - let Some(frame_def_id) = frame.query.ty_def_id else { - continue; - }; - let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else { - continue; - }; - let frame_span = frame - .query - .default_span(cycle_error.cycle[(i + 1) % cycle_error.cycle.len()].span); - if frame_span.is_dummy() { - continue; - } - if i == 0 { - diag.span_label(frame_span, "recursive call here"); - } else { - let coroutine_span = if frame_coroutine_kind.is_fn_like() { - tcx.def_span(tcx.parent(frame_def_id)) + let mut cycle: VecDeque<_> = cycle_error.cycle.iter().collect(); + + let guar = 'search: { + for _ in 0..cycle.len() { + if cycle[0].query.dep_kind == dep_kinds::layout_of + && let Some(def_id) = cycle[0].query.ty_def_id + && let Some(def_id) = def_id.as_local() + && let def_kind = tcx.def_kind(def_id) + && matches!(def_kind, DefKind::Closure) + && let Some(coroutine_kind) = tcx.coroutine_kind(def_id) + { + // FIXME: `def_span` for an fn-like coroutine will point to the fn's body + // due to interactions between the desugaring into a closure expr and the + // def_span code. I'm not motivated to fix it, because I tried and it was + // not working, so just hack around it by grabbing the parent fn's span. + let span = if coroutine_kind.is_fn_like() { + tcx.def_span(tcx.local_parent(def_id)) } else { - tcx.def_span(frame_def_id) + tcx.def_span(def_id) }; - let mut multispan = MultiSpan::from_span(coroutine_span); - multispan.push_span_label(frame_span, "...leading to this recursive call"); - diag.span_note( - multispan, - format!("which leads to this {}", tcx.def_descr(frame_def_id)), + let mut diag = struct_span_err!( + tcx.sess.dcx(), + span, + E0733, + "recursion in {} {} requires boxing", + tcx.def_kind_descr_article(def_kind, def_id.to_def_id()), + tcx.def_kind_descr(def_kind, def_id.to_def_id()), ); + for (i, frame) in cycle.iter().enumerate() { + if frame.query.dep_kind != dep_kinds::layout_of { + continue; + } + let Some(frame_def_id) = frame.query.ty_def_id else { + continue; + }; + let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else { + continue; + }; + let frame_span = + frame.query.default_span(cycle[(i + 1) % cycle.len()].span); + if frame_span.is_dummy() { + continue; + } + if i == 0 { + diag.span_label(frame_span, "recursive call here"); + } else { + let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() { + tcx.def_span(tcx.parent(frame_def_id)) + } else { + tcx.def_span(frame_def_id) + }; + let mut multispan = MultiSpan::from_span(coroutine_span); + multispan + .push_span_label(frame_span, "...leading to this recursive call"); + diag.span_note( + multispan, + format!("which leads to this {}", tcx.def_descr(frame_def_id)), + ); + } + } + // FIXME: We could report a structured suggestion if we had + // enough info here... Maybe we can use a hacky HIR walker. + if matches!( + coroutine_kind, + hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) + ) { + diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future"); + } + break 'search diag.emit(); + } else { + cycle.rotate_left(1); } } - if matches!( - coroutine_kind, - hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) - ) { - diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future"); - diag.note( - "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion", - ); - } - diag.emit() - } else { report_cycle(tcx.sess, cycle_error).emit() }; diff --git a/tests/ui/async-await/in-trait/async-recursive-generic.stderr b/tests/ui/async-await/in-trait/async-recursive-generic.stderr index 37de274565d3..d085747bc4bf 100644 --- a/tests/ui/async-await/in-trait/async-recursive-generic.stderr +++ b/tests/ui/async-await/in-trait/async-recursive-generic.stderr @@ -8,7 +8,6 @@ LL | self.foo_recursive(n - 1).await | ------------------------------- recursive call here | = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future - = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error diff --git a/tests/ui/async-await/in-trait/async-recursive.stderr b/tests/ui/async-await/in-trait/async-recursive.stderr index 6b99c516c3b5..25ebc6e77c4f 100644 --- a/tests/ui/async-await/in-trait/async-recursive.stderr +++ b/tests/ui/async-await/in-trait/async-recursive.stderr @@ -8,7 +8,6 @@ LL | self.foo_recursive(n - 1).await | ------------------------------- recursive call here | = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future - = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs index 8443cbcf4ac8..4b615343a05f 100644 --- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs +++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs @@ -1,6 +1,7 @@ // edition: 2021 + +// Test doesn't fail until monomorphization time, unfortunately. // build-fail -//~^^ ERROR cycle detected when computing layout of fn main() { let _ = async { @@ -31,6 +32,7 @@ where C: First, { async fn second(self) { + //~^ ERROR recursion in an async fn requires boxing self.first().await.second().await; } } diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr index 8e573b512ad4..8126c6e13942 100644 --- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr @@ -1,12 +1,11 @@ -error[E0391]: cycle detected when computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>` +error[E0733]: recursion in an async fn requires boxing + --> $DIR/indirect-recursion-issue-112047.rs:34:5 | - = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`... - = note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`... - = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<::Second as Second>::{opaque#0}>`... - = note: ...which again requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`, completing the cycle - = note: cycle used when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:6:13: 8:6}` - = 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 +LL | async fn second(self) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0391`. +For more information about this error, try `rustc --explain E0733`. diff --git a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr index 3f2ee4150dae..52fb41be1fbc 100644 --- a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr +++ b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr @@ -14,7 +14,6 @@ LL | async fn rec_2() { LL | rec_1().await; | ------------- ...leading to this recursive call = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future - = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error diff --git a/tests/ui/async-await/recursive-async-impl-trait-type.stderr b/tests/ui/async-await/recursive-async-impl-trait-type.stderr index cec92c54f01f..5475469335fb 100644 --- a/tests/ui/async-await/recursive-async-impl-trait-type.stderr +++ b/tests/ui/async-await/recursive-async-impl-trait-type.stderr @@ -8,7 +8,6 @@ LL | recursive_async_function().await; | -------------------------------- recursive call here | = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future - = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/recursive-coroutine.rs b/tests/ui/impl-trait/recursive-coroutine-boxed.rs similarity index 100% rename from tests/ui/impl-trait/recursive-coroutine.rs rename to tests/ui/impl-trait/recursive-coroutine-boxed.rs diff --git a/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr b/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr new file mode 100644 index 000000000000..11b3c4ef0078 --- /dev/null +++ b/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr @@ -0,0 +1,11 @@ +error[E0733]: recursion in a coroutine requires boxing + --> $DIR/recursive-coroutine-indirect.rs:6:5 + | +LL | move || { + | ^^^^^^^ +LL | let x = coroutine_hold(); + | - recursive call here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0733`. diff --git a/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr b/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr new file mode 100644 index 000000000000..11b3c4ef0078 --- /dev/null +++ b/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr @@ -0,0 +1,11 @@ +error[E0733]: recursion in a coroutine requires boxing + --> $DIR/recursive-coroutine-indirect.rs:6:5 + | +LL | move || { + | ^^^^^^^ +LL | let x = coroutine_hold(); + | - recursive call here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0733`. diff --git a/tests/ui/impl-trait/recursive-coroutine-indirect.rs b/tests/ui/impl-trait/recursive-coroutine-indirect.rs new file mode 100644 index 000000000000..4f8d4d330505 --- /dev/null +++ b/tests/ui/impl-trait/recursive-coroutine-indirect.rs @@ -0,0 +1,13 @@ +// revisions: current next +//[next] compile-flags: -Znext-solver +#![feature(coroutines)] +#![allow(unconditional_recursion)] +fn coroutine_hold() -> impl Sized { + move || { //~ ERROR recursion in a coroutine requires boxing + let x = coroutine_hold(); + yield; + x; + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs index a6bca107b1ec..432f80a1763e 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs @@ -1,6 +1,5 @@ // Test that impl trait does not allow creating recursive types that are // otherwise forbidden. - #![feature(coroutines)] #![allow(unconditional_recursion)] @@ -69,15 +68,6 @@ fn substs_change() -> impl Sized { (substs_change::<&T>(),) } -fn coroutine_hold() -> impl Sized { - move || { - //~^ ERROR - let x = coroutine_hold(); - yield; - x; - } -} - fn use_fn_ptr() -> impl Sized { // OK, error already reported fn_ptr() diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 16a9012958e9..d5b8c531fd6e 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -1,5 +1,5 @@ error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:7:22 + --> $DIR/recursive-impl-trait-type-indirect.rs:6:22 | LL | fn option(i: i32) -> impl Sized { | ^^^^^^^^^^ recursive opaque type @@ -10,7 +10,7 @@ LL | if i < 0 { None } else { Some((option(i - 1), i)) } | returning here with type `Option<(impl Sized, i32)>` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:12:15 + --> $DIR/recursive-impl-trait-type-indirect.rs:11:15 | LL | fn tuple() -> impl Sized { | ^^^^^^^^^^ recursive opaque type @@ -19,7 +19,7 @@ LL | (tuple(),) | ---------- returning here with type `(impl Sized,)` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:17:15 + --> $DIR/recursive-impl-trait-type-indirect.rs:16:15 | LL | fn array() -> impl Sized { | ^^^^^^^^^^ recursive opaque type @@ -28,7 +28,7 @@ LL | [array()] | --------- returning here with type `[impl Sized; 1]` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:22:13 + --> $DIR/recursive-impl-trait-type-indirect.rs:21:13 | LL | fn ptr() -> impl Sized { | ^^^^^^^^^^ recursive opaque type @@ -37,7 +37,7 @@ LL | &ptr() as *const _ | ------------------ returning here with type `*const impl Sized` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:27:16 + --> $DIR/recursive-impl-trait-type-indirect.rs:26:16 | LL | fn fn_ptr() -> impl Sized { | ^^^^^^^^^^ recursive opaque type @@ -46,7 +46,7 @@ LL | fn_ptr as fn() -> _ | ------------------- returning here with type `fn() -> impl Sized` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:32:25 + --> $DIR/recursive-impl-trait-type-indirect.rs:31:25 | LL | fn closure_capture() -> impl Sized { | ^^^^^^^^^^ recursive opaque type @@ -55,10 +55,10 @@ LL | / move || { LL | | x; | | - closure captures itself here LL | | } - | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 35:12}` + | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:34:5: 34:12}` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:40:29 + --> $DIR/recursive-impl-trait-type-indirect.rs:39:29 | LL | fn closure_ref_capture() -> impl Sized { | ^^^^^^^^^^ recursive opaque type @@ -67,28 +67,28 @@ LL | / move || { LL | | &x; | | - closure captures itself here LL | | } - | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 43:12}` + | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:42:5: 42:12}` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:48:21 + --> $DIR/recursive-impl-trait-type-indirect.rs:47:21 | LL | fn closure_sig() -> impl Sized { | ^^^^^^^^^^ recursive opaque type LL | LL | || closure_sig() - | ---------------- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:7}` + | ---------------- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:49:5: 49:7}` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:53:23 + --> $DIR/recursive-impl-trait-type-indirect.rs:52:23 | LL | fn coroutine_sig() -> impl Sized { | ^^^^^^^^^^ recursive opaque type LL | LL | || coroutine_sig() - | ------------------ returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:55:5: 55:7}` + | ------------------ returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:54:5: 54:7}` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:58:27 + --> $DIR/recursive-impl-trait-type-indirect.rs:57:27 | LL | fn coroutine_capture() -> impl Sized { | ^^^^^^^^^^ recursive opaque type @@ -98,10 +98,10 @@ LL | | yield; LL | | x; | | - coroutine captures itself here LL | | } - | |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 61:12}` + | |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:60:5: 60:12}` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:67:35 + --> $DIR/recursive-impl-trait-type-indirect.rs:66:35 | LL | fn substs_change() -> impl Sized { | ^^^^^^^^^^ recursive opaque type @@ -109,17 +109,8 @@ LL | LL | (substs_change::<&T>(),) | ------------------------ returning here with type `(impl Sized,)` -error[E0733]: recursion in a coroutine requires boxing - --> $DIR/recursive-impl-trait-type-indirect.rs:73:5 - | -LL | move || { - | ^^^^^^^ -LL | -LL | let x = coroutine_hold(); - | - recursive call here - error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:86:26 + --> $DIR/recursive-impl-trait-type-indirect.rs:76:26 | LL | fn mutual_recursion() -> impl Sync { | ^^^^^^^^^ recursive opaque type @@ -131,7 +122,7 @@ LL | fn mutual_recursion_b() -> impl Sized { | ---------- returning this opaque type `impl Sized` error[E0720]: cannot resolve opaque type - --> $DIR/recursive-impl-trait-type-indirect.rs:91:28 + --> $DIR/recursive-impl-trait-type-indirect.rs:81:28 | LL | fn mutual_recursion() -> impl Sync { | --------- returning this opaque type `impl Sync` @@ -142,7 +133,6 @@ LL | LL | mutual_recursion() | ------------------ returning here with type `impl Sync` -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors -Some errors have detailed explanations: E0720, E0733. -For more information about an error, try `rustc --explain E0720`. +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr index 2e75f676971c..b62186103c7c 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr @@ -14,7 +14,6 @@ LL | async fn recur(t: impl Recur) { LL | t.recur().await; | --------------- ...leading to this recursive call = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future - = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion error: aborting due to 1 previous error From 9a756034a981b0aa15598f990381203dffe96a1c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 8 Jan 2024 14:31:25 +0000 Subject: [PATCH 236/257] Last nits --- .../src/error_codes/E0733.md | 38 +++++------------- compiler/rustc_middle/src/values.rs | 40 ++++++++++++++----- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0733.md b/compiler/rustc_error_codes/src/error_codes/E0733.md index cceb0880350e..42c01975dd8d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0733.md +++ b/compiler/rustc_error_codes/src/error_codes/E0733.md @@ -10,48 +10,30 @@ async fn foo(n: usize) { } ``` -To perform async recursion, the `async fn` needs to be desugared such that the -`Future` is explicit in the return type: - -```edition2018,compile_fail,E0733 -use std::future::Future; -fn foo_desugared(n: usize) -> impl Future { - async move { - if n > 0 { - foo_desugared(n - 1).await; - } - } -} -``` - -Finally, the future is wrapped in a pinned box: +The recursive invocation can be boxed: ```edition2018 -use std::future::Future; -use std::pin::Pin; -fn foo_recursive(n: usize) -> Pin>> { - Box::pin(async move { - if n > 0 { - foo_recursive(n - 1).await; - } - }) +async fn foo(n: usize) { + if n > 0 { + Box::pin(foo(n - 1)).await; + } } ``` The `Box<...>` ensures that the result is of known size, and the pin is required to keep it in the same place in memory. -Alternatively, the recursive call-site can be boxed: +Alternatively, the body can be boxed: ```edition2018 use std::future::Future; use std::pin::Pin; -fn foo_recursive(n: usize) -> impl Future { - async move { +fn foo(n: usize) -> Pin>> { + Box::pin(async move { if n > 0 { - Box::pin(foo_recursive(n - 1)).await; + foo(n - 1).await; } - } + }) } ``` diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 77e4ed06eaa2..27d04dbe3314 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -13,6 +13,7 @@ use rustc_span::{ErrorGuaranteed, Span}; use std::collections::VecDeque; use std::fmt::Write; +use std::ops::ControlFlow; impl<'tcx> Value> for Ty<'_> { fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { @@ -130,16 +131,34 @@ impl<'tcx> Value> for ty::EarlyBinder> } } +// Take a cycle of `Q` and try `try_cycle` on every permutation, falling back to `otherwise`. +fn search_for_cycle_permutation( + cycle: &[Q], + try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow, + otherwise: impl FnOnce() -> T, +) -> T { + let mut cycle: VecDeque<_> = cycle.iter().collect(); + for _ in 0..cycle.len() { + match try_cycle(&mut cycle) { + ControlFlow::Continue(_) => { + cycle.rotate_left(1); + } + ControlFlow::Break(t) => return t, + } + } + + otherwise() +} + impl<'tcx, T> Value> for Result> { fn from_cycle_error( tcx: TyCtxt<'tcx>, cycle_error: &CycleError, _guar: ErrorGuaranteed, ) -> Self { - let mut cycle: VecDeque<_> = cycle_error.cycle.iter().collect(); - - let guar = 'search: { - for _ in 0..cycle.len() { + let diag = search_for_cycle_permutation( + &cycle_error.cycle, + |cycle| { if cycle[0].query.dep_kind == dep_kinds::layout_of && let Some(def_id) = cycle[0].query.ty_def_id && let Some(def_id) = def_id.as_local() @@ -204,13 +223,16 @@ impl<'tcx, T> Value> for Result> ) { diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future"); } - break 'search diag.emit(); + + ControlFlow::Break(diag) } else { - cycle.rotate_left(1); + ControlFlow::Continue(()) } - } - report_cycle(tcx.sess, cycle_error).emit() - }; + }, + || report_cycle(tcx.sess, cycle_error), + ); + + let guar = diag.emit(); // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really, From 835680286207b633f4727310829a709b585f7b56 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 20 May 2023 08:21:56 +0000 Subject: [PATCH 237/257] Move promote_consts back to rustc_mir_transform. --- .../rustc_const_eval/src/transform/mod.rs | 1 - compiler/rustc_mir_transform/src/lib.rs | 2 +- .../src}/promote_consts.rs | 19 ++++++------------- 3 files changed, 7 insertions(+), 15 deletions(-) rename compiler/{rustc_const_eval/src/transform => rustc_mir_transform/src}/promote_consts.rs (99%) diff --git a/compiler/rustc_const_eval/src/transform/mod.rs b/compiler/rustc_const_eval/src/transform/mod.rs index a2928bdf51b8..e3582c7d3174 100644 --- a/compiler/rustc_const_eval/src/transform/mod.rs +++ b/compiler/rustc_const_eval/src/transform/mod.rs @@ -1,3 +1,2 @@ pub mod check_consts; -pub mod promote_consts; pub mod validate; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index f5f51c0ec8ad..dfc4ff3b7a35 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -93,6 +93,7 @@ mod multiple_return_terminators; mod normalize_array_len; mod nrvo; mod prettify; +mod promote_consts; mod ref_prop; mod remove_noop_landing_pads; mod remove_storage_markers; @@ -114,7 +115,6 @@ mod uninhabited_enum_branching; mod unreachable_prop; use rustc_const_eval::transform::check_consts::{self, ConstCx}; -use rustc_const_eval::transform::promote_consts; use rustc_const_eval::transform::validate; use rustc_mir_dataflow::rustc_peek; diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs similarity index 99% rename from compiler/rustc_const_eval/src/transform/promote_consts.rs rename to compiler/rustc_mir_transform/src/promote_consts.rs index 155cf4ff9e2c..9a60e83d322f 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -25,7 +25,7 @@ use rustc_index::{Idx, IndexSlice, IndexVec}; use std::cell::Cell; use std::{cmp, iter, mem}; -use crate::transform::check_consts::{qualifs, ConstCx}; +use rustc_const_eval::transform::check_consts::{qualifs, ConstCx}; /// A `MirPass` for promotion. /// @@ -64,7 +64,7 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { /// State of a temporary during collection and promotion. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum TempState { +enum TempState { /// No references to this temp. Undefined, /// One direct assignment and any number of direct uses. @@ -78,18 +78,11 @@ pub enum TempState { PromotedOut, } -impl TempState { - pub fn is_promotable(&self) -> bool { - debug!("is_promotable: self={:?}", self); - matches!(self, TempState::Defined { .. }) - } -} - /// A "root candidate" for promotion, which will become the /// returned value in a promoted MIR, unless it's a subset /// of a larger candidate. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Candidate { +struct Candidate { location: Location, } @@ -162,7 +155,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { } } -pub fn collect_temps_and_candidates<'tcx>( +fn collect_temps_and_candidates<'tcx>( ccx: &ConstCx<'_, 'tcx>, ) -> (IndexVec, Vec) { let mut collector = Collector { @@ -676,7 +669,7 @@ impl<'tcx> Validator<'_, 'tcx> { } // FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`. -pub fn validate_candidates( +fn validate_candidates( ccx: &ConstCx<'_, '_>, temps: &mut IndexSlice, candidates: &[Candidate], @@ -930,7 +923,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> { } } -pub fn promote_candidates<'tcx>( +fn promote_candidates<'tcx>( body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>, mut temps: IndexVec, From cae0dc28332065dfa3fa168fdc1f2818bcbdf0c3 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 20 May 2023 08:54:16 +0000 Subject: [PATCH 238/257] Simplify code flow. --- compiler/rustc_mir_transform/src/lib.rs | 1 + .../rustc_mir_transform/src/promote_consts.rs | 378 ++++++++---------- 2 files changed, 157 insertions(+), 222 deletions(-) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index dfc4ff3b7a35..915ddcb2775d 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,5 +1,6 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![feature(assert_matches)] #![feature(box_patterns)] #![feature(cow_is_borrowed)] #![feature(decl_macro)] diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9a60e83d322f..9b9c9aae88bf 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -12,6 +12,7 @@ //! initialization and can otherwise silence errors, if //! move analysis runs after promotion on broken MIR. +use either::{Left, Right}; use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -22,6 +23,7 @@ use rustc_span::Span; use rustc_index::{Idx, IndexSlice, IndexVec}; +use std::assert_matches::assert_matches; use std::cell::Cell; use std::{cmp, iter, mem}; @@ -116,41 +118,38 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { let temp = &mut self.temps[index]; debug!("visit_local: temp={:?}", temp); - if *temp == TempState::Undefined { - match context { + *temp = match *temp { + TempState::Undefined => match context { PlaceContext::MutatingUse(MutatingUseContext::Store) | PlaceContext::MutatingUse(MutatingUseContext::Call) => { - *temp = TempState::Defined { location, uses: 0, valid: Err(()) }; + TempState::Defined { location, uses: 0, valid: Err(()) } + } + _ => TempState::Unpromotable, + }, + TempState::Defined { ref mut uses, .. } => { + // We always allow borrows, even mutable ones, as we need + // to promote mutable borrows of some ZSTs e.g., `&mut []`. + let allowed_use = match context { + PlaceContext::MutatingUse(MutatingUseContext::Borrow) + | PlaceContext::NonMutatingUse(_) => true, + PlaceContext::MutatingUse(_) | PlaceContext::NonUse(_) => false, + }; + debug!("visit_local: allowed_use={:?}", allowed_use); + if allowed_use { + *uses += 1; return; } - _ => { /* mark as unpromotable below */ } + TempState::Unpromotable } - } else if let TempState::Defined { uses, .. } = temp { - // We always allow borrows, even mutable ones, as we need - // to promote mutable borrows of some ZSTs e.g., `&mut []`. - let allowed_use = match context { - PlaceContext::MutatingUse(MutatingUseContext::Borrow) - | PlaceContext::NonMutatingUse(_) => true, - PlaceContext::MutatingUse(_) | PlaceContext::NonUse(_) => false, - }; - debug!("visit_local: allowed_use={:?}", allowed_use); - if allowed_use { - *uses += 1; - return; - } - /* mark as unpromotable below */ - } - *temp = TempState::Unpromotable; + _ => TempState::Unpromotable, + }; } fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { self.super_rvalue(rvalue, location); - match *rvalue { - Rvalue::Ref(..) => { - self.candidates.push(Candidate { location }); - } - _ => {} + if let Rvalue::Ref(..) = *rvalue { + self.candidates.push(Candidate { location }); } } } @@ -189,230 +188,165 @@ struct Unpromotable; impl<'tcx> Validator<'_, 'tcx> { fn validate_candidate(&mut self, candidate: Candidate) -> Result<(), Unpromotable> { - let loc = candidate.location; - let statement = &self.body[loc.block].statements[loc.statement_index]; - match &statement.kind { - StatementKind::Assign(box (_, Rvalue::Ref(_, kind, place))) => { - // We can only promote interior borrows of promotable temps (non-temps - // don't get promoted anyway). - self.validate_local(place.local)?; + let Left(statement) = self.body.stmt_at(candidate.location) else { bug!() }; + let Some((_, Rvalue::Ref(_, kind, place))) = statement.kind.as_assign() else { bug!() }; - // The reference operation itself must be promotable. - // (Needs to come after `validate_local` to avoid ICEs.) - self.validate_ref(*kind, place)?; + // We can only promote interior borrows of promotable temps (non-temps + // don't get promoted anyway). + self.validate_local(place.local)?; - // We do not check all the projections (they do not get promoted anyway), - // but we do stay away from promoting anything involving a dereference. - if place.projection.contains(&ProjectionElem::Deref) { - return Err(Unpromotable); - } + // The reference operation itself must be promotable. + // (Needs to come after `validate_local` to avoid ICEs.) + self.validate_ref(*kind, place)?; - Ok(()) - } - _ => bug!(), + // We do not check all the projections (they do not get promoted anyway), + // but we do stay away from promoting anything involving a dereference. + if place.projection.contains(&ProjectionElem::Deref) { + return Err(Unpromotable); } + + Ok(()) } // FIXME(eddyb) maybe cache this? fn qualif_local(&mut self, local: Local) -> bool { - if let TempState::Defined { location: loc, .. } = self.temps[local] { - let num_stmts = self.body[loc.block].statements.len(); + let TempState::Defined { location: loc, .. } = self.temps[local] else { + return false; + }; - if loc.statement_index < num_stmts { - let statement = &self.body[loc.block].statements[loc.statement_index]; - match &statement.kind { - StatementKind::Assign(box (_, rhs)) => qualifs::in_rvalue::( - self.ccx, - &mut |l| self.qualif_local::(l), - rhs, - ), - _ => { - span_bug!( - statement.source_info.span, - "{:?} is not an assignment", - statement - ); - } - } - } else { - let terminator = self.body[loc.block].terminator(); - match &terminator.kind { - TerminatorKind::Call { .. } => { - let return_ty = self.body.local_decls[local].ty; - Q::in_any_value_of_ty(self.ccx, return_ty) - } - kind => { - span_bug!(terminator.source_info.span, "{:?} not promotable", kind); - } - } + let stmt_or_term = self.body.stmt_at(loc); + match stmt_or_term { + Left(statement) => { + let Some((_, rhs)) = statement.kind.as_assign() else { + span_bug!(statement.source_info.span, "{:?} is not an assignment", statement) + }; + qualifs::in_rvalue::(self.ccx, &mut |l| self.qualif_local::(l), rhs) + } + Right(terminator) => { + assert_matches!(terminator.kind, TerminatorKind::Call { .. }); + let return_ty = self.body.local_decls[local].ty; + Q::in_any_value_of_ty(self.ccx, return_ty) } - } else { - false } } fn validate_local(&mut self, local: Local) -> Result<(), Unpromotable> { - if let TempState::Defined { location: loc, uses, valid } = self.temps[local] { - // We cannot promote things that need dropping, since the promoted value - // would not get dropped. - if self.qualif_local::(local) { - return Err(Unpromotable); - } - valid.or_else(|_| { - let ok = { - let block = &self.body[loc.block]; - let num_stmts = block.statements.len(); + let TempState::Defined { location: loc, uses, valid } = self.temps[local] else { + return Err(Unpromotable); + }; - if loc.statement_index < num_stmts { - let statement = &block.statements[loc.statement_index]; - match &statement.kind { - StatementKind::Assign(box (_, rhs)) => self.validate_rvalue(rhs), - _ => { - span_bug!( - statement.source_info.span, - "{:?} is not an assignment", - statement - ); - } - } - } else { - let terminator = block.terminator(); - match &terminator.kind { - TerminatorKind::Call { func, args, .. } => { - self.validate_call(func, args) - } - TerminatorKind::Yield { .. } => Err(Unpromotable), - kind => { - span_bug!(terminator.source_info.span, "{:?} not promotable", kind); - } - } - } - }; - self.temps[local] = match ok { - Ok(()) => TempState::Defined { location: loc, uses, valid: Ok(()) }, - Err(_) => TempState::Unpromotable, - }; - ok - }) - } else { - Err(Unpromotable) + // We cannot promote things that need dropping, since the promoted value would not get + // dropped. + if self.qualif_local::(local) { + return Err(Unpromotable); } + + if valid.is_ok() { + return Ok(()); + } + + let ok = { + let stmt_or_term = self.body.stmt_at(loc); + match stmt_or_term { + Left(statement) => { + let Some((_, rhs)) = statement.kind.as_assign() else { + span_bug!( + statement.source_info.span, + "{:?} is not an assignment", + statement + ) + }; + self.validate_rvalue(rhs) + } + Right(terminator) => match &terminator.kind { + TerminatorKind::Call { func, args, .. } => self.validate_call(func, args), + TerminatorKind::Yield { .. } => Err(Unpromotable), + kind => { + span_bug!(terminator.source_info.span, "{:?} not promotable", kind); + } + }, + } + }; + + self.temps[local] = match ok { + Ok(()) => TempState::Defined { location: loc, uses, valid: Ok(()) }, + Err(_) => TempState::Unpromotable, + }; + + ok } fn validate_place(&mut self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { - match place.last_projection() { - None => self.validate_local(place.local), - Some((place_base, elem)) => { - // Validate topmost projection, then recurse. - match elem { - ProjectionElem::Deref => { - let mut promotable = false; - // When a static is used by-value, that gets desugared to `*STATIC_ADDR`, - // and we need to be able to promote this. So check if this deref matches - // that specific pattern. + let Some((place_base, elem)) = place.last_projection() else { + return self.validate_local(place.local); + }; - // We need to make sure this is a `Deref` of a local with no further projections. - // Discussion can be found at - // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247 - if let Some(local) = place_base.as_local() { - if let TempState::Defined { location, .. } = self.temps[local] { - let def_stmt = self.body[location.block] - .statements - .get(location.statement_index); - if let Some(Statement { - kind: - StatementKind::Assign(box ( - _, - Rvalue::Use(Operand::Constant(c)), - )), - .. - }) = def_stmt - { - if let Some(did) = c.check_static_ptr(self.tcx) { - // Evaluating a promoted may not read statics except if it got - // promoted from a static (this is a CTFE check). So we - // can only promote static accesses inside statics. - if let Some(hir::ConstContext::Static(..)) = self.const_kind - { - if !self.tcx.is_thread_local_static(did) { - promotable = true; - } - } - } - } - } - } - if !promotable { - return Err(Unpromotable); - } - } - ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => { - return Err(Unpromotable); - } + // Validate topmost projection, then recurse. + match elem { + // Recurse directly. + ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subtype(_) + | ProjectionElem::Subslice { .. } => {} - ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subtype(_) - | ProjectionElem::Subslice { .. } => {} + // Never recurse. + ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => { + return Err(Unpromotable); + } - ProjectionElem::Index(local) => { - let mut promotable = false; - // Only accept if we can predict the index and are indexing an array. - let val = if let TempState::Defined { location: loc, .. } = - self.temps[local] - { - let block = &self.body[loc.block]; - if loc.statement_index < block.statements.len() { - let statement = &block.statements[loc.statement_index]; - match &statement.kind { - StatementKind::Assign(box ( - _, - Rvalue::Use(Operand::Constant(c)), - )) => c.const_.try_eval_target_usize(self.tcx, self.param_env), - _ => None, - } - } else { - None - } - } else { - None - }; - if let Some(idx) = val { - // Determine the type of the thing we are indexing. - let ty = place_base.ty(self.body, self.tcx).ty; - match ty.kind() { - ty::Array(_, len) => { - // It's an array; determine its length. - if let Some(len) = - len.try_eval_target_usize(self.tcx, self.param_env) - { - // If the index is in-bounds, go ahead. - if idx < len { - promotable = true; - } - } - } - _ => {} - } - } - if !promotable { - return Err(Unpromotable); - } + ProjectionElem::Deref => { + // When a static is used by-value, that gets desugared to `*STATIC_ADDR`, + // and we need to be able to promote this. So check if this deref matches + // that specific pattern. - self.validate_local(local)?; - } - - ProjectionElem::Field(..) => { - let base_ty = place_base.ty(self.body, self.tcx).ty; - if base_ty.is_union() { - // No promotion of union field accesses. - return Err(Unpromotable); - } - } + // We need to make sure this is a `Deref` of a local with no further projections. + // Discussion can be found at + // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247 + if let Some(local) = place_base.as_local() + && let TempState::Defined { location, .. } = self.temps[local] + && let Left(def_stmt) = self.body.stmt_at(location) + && let Some((_, Rvalue::Use(Operand::Constant(c)))) = def_stmt.kind.as_assign() + && let Some(did) = c.check_static_ptr(self.tcx) + // Evaluating a promoted may not read statics except if it got + // promoted from a static (this is a CTFE check). So we + // can only promote static accesses inside statics. + && let Some(hir::ConstContext::Static(..)) = self.const_kind + && !self.tcx.is_thread_local_static(did) + { + // Recurse. + } else { + return Err(Unpromotable); } + } + ProjectionElem::Index(local) => { + // Only accept if we can predict the index and are indexing an array. + if let TempState::Defined { location: loc, .. } = self.temps[local] + && let Left(statement) = self.body.stmt_at(loc) + && let Some((_, Rvalue::Use(Operand::Constant(c)))) = statement.kind.as_assign() + && let Some(idx) = c.const_.try_eval_target_usize(self.tcx, self.param_env) + // Determine the type of the thing we are indexing. + && let ty::Array(_, len) = place_base.ty(self.body, self.tcx).ty.kind() + // It's an array; determine its length. + && let Some(len) = len.try_eval_target_usize(self.tcx, self.param_env) + // If the index is in-bounds, go ahead. + && idx < len + { + self.validate_local(local)?; + // Recurse. + } else { + return Err(Unpromotable); + } + } - self.validate_place(place_base) + ProjectionElem::Field(..) => { + let base_ty = place_base.ty(self.body, self.tcx).ty; + if base_ty.is_union() { + // No promotion of union field accesses. + return Err(Unpromotable); + } } } + + self.validate_place(place_base) } fn validate_operand(&mut self, operand: &Operand<'tcx>) -> Result<(), Unpromotable> { From 5d6463c26c0f20f855da4ca54caa583c2258fb2f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 8 Jan 2024 22:40:32 +0000 Subject: [PATCH 239/257] Make match exhaustive. --- compiler/rustc_mir_transform/src/promote_consts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9b9c9aae88bf..841b86fed454 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -141,7 +141,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { } TempState::Unpromotable } - _ => TempState::Unpromotable, + TempState::Unpromotable | TempState::PromotedOut => TempState::Unpromotable, }; } From a2b765fc37e2de38502912aea051d3fdde42357c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 8 Jan 2024 09:43:58 +1100 Subject: [PATCH 240/257] Remove `-Zdont-buffer-diagnostics`. It was added in #54232. It seems like it was aimed at NLL development, which is well in the past. Also, it looks like `-Ztreat-err-as-bug` can be used to achieve the same effect. So it doesn't seem necessary. --- compiler/rustc_errors/src/diagnostic_builder.rs | 3 +-- compiler/rustc_errors/src/lib.rs | 3 --- compiler/rustc_interface/src/tests.rs | 1 - compiler/rustc_session/src/config.rs | 1 - compiler/rustc_session/src/options.rs | 3 --- 5 files changed, 1 insertion(+), 10 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index e72791d89a29..3789cdaf354a 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -266,8 +266,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// Converts the builder to a `Diagnostic` for later emission, /// unless dcx has disabled such buffering. pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> { - let flags = self.dcx.inner.lock().flags; - if flags.dont_buffer_diagnostics || flags.treat_err_as_bug.is_some() { + if self.dcx.inner.lock().flags.treat_err_as_bug.is_some() { self.emit(); return None; } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index b97ec02675a6..76b7e0d79a98 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -524,9 +524,6 @@ pub struct DiagCtxtFlags { /// If Some, the Nth error-level diagnostic is upgraded to bug-level. /// (rustc: see `-Z treat-err-as-bug`) pub treat_err_as_bug: Option, - /// If true, immediately emit diagnostics that would otherwise be buffered. - /// (rustc: see `-Z dont-buffer-diagnostics` and `-Z treat-err-as-bug`) - pub dont_buffer_diagnostics: bool, /// Show macro backtraces. /// (rustc: see `-Z macro-backtrace`) pub macro_backtrace: bool, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index f3f59b05682d..444e3700cf77 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -659,7 +659,6 @@ fn test_unstable_options_tracking_hash() { // tidy-alphabetical-start untracked!(assert_incr_state, Some(String::from("loaded"))); untracked!(deduplicate_diagnostics, false); - untracked!(dont_buffer_diagnostics, true); untracked!(dump_dep_graph, true); untracked!(dump_mir, Some(String::from("abc"))); untracked!(dump_mir_dataflow, true); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 12d8293ecd2f..61796d7a6caf 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1146,7 +1146,6 @@ impl UnstableOptions { DiagCtxtFlags { can_emit_warnings, treat_err_as_bug: self.treat_err_as_bug, - dont_buffer_diagnostics: self.dont_buffer_diagnostics, macro_backtrace: self.macro_backtrace, deduplicate_diagnostics: self.deduplicate_diagnostics, track_diagnostics: self.track_diagnostics, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 842bf1d60f66..6d96ec4d6f67 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1553,9 +1553,6 @@ options! { dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], "in dep-info output, omit targets for tracking dependencies of the dep-info files \ themselves (default: no)"), - dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], - "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \ - (default: no)"), dual_proc_macros: bool = (false, parse_bool, [TRACKED], "load proc macros for both target and host, but only link to the target (default: no)"), dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], From c3cd657210403b9b8b0e8557bc1604d1890e6821 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 8 Jan 2024 14:29:55 -0700 Subject: [PATCH 241/257] rustdoc-search: intern function search types This takes advantage of more reuse opportunities. Along with the empty object commit, they bringing memory usage down about 20% over the original. --- src/librustdoc/html/static/js/search.js | 89 ++++++++++++++++++++----- 1 file changed, 72 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 5bd375a86a78..7995a33f09f9 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2725,17 +2725,25 @@ ${item.displayPath}${name}\ /** * Empty, immutable map used in item search types with no bindings. * - * @type {Map>} + * @type {Map>} */ const EMPTY_BINDINGS_MAP = new Map(); /** * Empty, immutable map used in item search types with no bindings. * - * @type {Array} + * @type {Array} */ const EMPTY_GENERICS_ARRAY = []; + /** + * Object pool for function types with no bindings or generics. + * This is reset after loading the index. + * + * @type {Map} + */ + let TYPES_POOL = new Map(); + /** * Converts a single type. * @@ -2778,35 +2786,80 @@ ${item.displayPath}${name}\ bindings = EMPTY_BINDINGS_MAP; } } + /** + * @type {FunctionType} + */ + let result; if (pathIndex < 0) { // types less than 0 are generic parameters // the actual names of generic parameters aren't stored, since they aren't API - return { + result = { id: pathIndex, ty: TY_GENERIC, path: null, generics, bindings, }; - } - if (pathIndex === 0) { + } else if (pathIndex === 0) { // `0` is used as a sentinel because it's fewer bytes than `null` - return { + result = { id: null, ty: null, path: null, generics, bindings, }; + } else { + const item = lowercasePaths[pathIndex - 1]; + result = { + id: buildTypeMapIndex(item.name, isAssocType), + ty: item.ty, + path: item.path, + generics, + bindings, + }; } - const item = lowercasePaths[pathIndex - 1]; - return { - id: buildTypeMapIndex(item.name, isAssocType), - ty: item.ty, - path: item.path, - generics, - bindings, - }; + const cr = TYPES_POOL.get(result.id); + if (cr) { + // Shallow equality check. Since this function is used + // to construct every type object, this should be mostly + // equivalent to a deep equality check, except if there's + // a conflict, we don't keep the old one around, so it's + // not a fully precise implementation of hashcons. + if (cr.generics.length === result.generics.length && + cr.generics !== result.generics && + cr.generics.every((x, i) => result.generics[i] === x) + ) { + result.generics = cr.generics; + } + if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) { + let ok = true; + for (const [k, v] of cr.bindings.entries()) { + const v2 = result.bindings.get(v); + if (!v2) { + ok = false; + break; + } + if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) { + result.bindings.set(k, v); + } else if (v !== v2) { + ok = false; + break; + } + } + if (ok) { + result.bindings = cr.bindings; + } + } + if (cr.ty === result.ty && cr.path === result.path + && cr.bindings === result.bindings && cr.generics === result.generics + && cr.ty === result.ty + ) { + return cr; + } + } + TYPES_POOL.set(result.id, result); + return result; } /** @@ -2817,7 +2870,7 @@ ${item.displayPath}${name}\ * object-based encoding so that the actual search code is more readable and easier to debug. * * The raw function search type format is generated using serde in - * librustdoc/html/render/mod.rs: impl Serialize for IndexItemFunctionType + * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string * * @param {{ * string: string, @@ -2986,8 +3039,8 @@ ${item.displayPath}${name}\ const fb = { id: null, ty: 0, - generics: [], - bindings: new Map(), + generics: EMPTY_GENERICS_ARRAY, + bindings: EMPTY_BINDINGS_MAP, }; for (const [k, v] of type.bindings.entries()) { fb.id = k; @@ -3215,6 +3268,8 @@ ${item.displayPath}${name}\ } currentIndex += itemTypes.length; } + // Drop the (rather large) hash table used for reusing function items + TYPES_POOL = new Map(); } /** From c8ded5260176495ee8012e8f162766897dfc611b Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Mon, 8 Jan 2024 21:36:02 -0500 Subject: [PATCH 242/257] GNU/Hurd: unconditionally use inline stack probes LLVM 11 has been unsupported since 45591408b18e7f93fcf8c09210c9a5a102d84b37, so this doesn't need to be conditional on the LLVM version. --- compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs index 9102673ef779..6884e078c27e 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs @@ -5,7 +5,7 @@ pub fn target() -> Target { base.cpu = "pentiumpro".into(); base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); - base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; + base.stack_probes = StackProbeType::Inline; Target { llvm_target: "i686-unknown-hurd-gnu".into(), From 36110147229f49024187df6b17175dd5810a26bc Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 22 Dec 2023 19:25:15 +0100 Subject: [PATCH 243/257] Rework and improve unstable documentation of check-cfg --- .../src/compiler-flags/check-cfg.md | 234 ++++++++++-------- 1 file changed, 128 insertions(+), 106 deletions(-) diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/unstable-book/src/compiler-flags/check-cfg.md index 8ab6e83d99e7..e8fc2fe986e2 100644 --- a/src/doc/unstable-book/src/compiler-flags/check-cfg.md +++ b/src/doc/unstable-book/src/compiler-flags/check-cfg.md @@ -4,18 +4,16 @@ The tracking issue for this feature is: [#82450](https://github.com/rust-lang/ru ------------------------ -This feature allows you to enable complete or partial checking of configuration. +This feature enables checking of conditional configuration. `rustc` accepts the `--check-cfg` option, which specifies whether to check conditions and how to -check them. The `--check-cfg` option takes a value, called the _check cfg specification_. The -check cfg specification is parsed using the Rust metadata syntax, just as the `--cfg` option is. +check them. The `--check-cfg` option takes a value, called the _check cfg specification_. +This specification has one form: -`--check-cfg` option take one form: +1. `--check-cfg cfg(...)` mark a configuration and it's expected values as expected. -1. `--check-cfg cfg(...)` enables checking the values within list-valued conditions. - -NOTE: No implicit expectation is added when using `--cfg` for both forms. Users are expected to -pass all expected names and values using `cfg(...)`. +*No implicit expectation is added when using `--cfg`. Users are expected to +pass all expected names and values using the _check cfg specification_.* ## The `cfg(...)` form @@ -23,7 +21,7 @@ The `cfg(...)` form enables checking the values within list-valued conditions. I basic form: ```bash -rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))' +rustc --check-cfg 'cfg(name, values("value1", "value2", ... "valueN"))' ``` where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal @@ -31,162 +29,186 @@ string. `name` specifies the name of the condition, such as `feature` or `my_cfg When the `cfg(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]` attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]` -and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the -list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs` -lint diagnostic. The default diagnostic level for this lint is `Warn`. +attribute and `cfg!(name = "value")` macro call. It will check that the `"value"` specified is +present in the list of expected values. If `"value"` is not in it, then `rustc` will report an +`unexpected_cfgs` lint diagnostic. The default diagnostic level for this lint is `Warn`. -The command line `--cfg` arguments are currently *NOT* checked but may very well be checked in -the future. +*The command line `--cfg` arguments are currently *NOT* checked but may very well be checked in +the future.* -To enable checking of values, but to provide an empty set of expected values, use these forms: +To enable checking of values, but to provide an *none*/empty set of expected values +(ie. expect `#[cfg(name)]`), use these forms: ```bash -rustc --check-cfg 'cfg(name1, ..., nameN)' -rustc --check-cfg 'cfg(name1, ..., nameN, values())' +rustc --check-cfg 'cfg(name)' +rustc --check-cfg 'cfg(name, values())' ``` To enable checking of name but not values (i.e. unknown expected values), use this form: ```bash -rustc --check-cfg 'cfg(name1, ..., nameN, values(any()))' +rustc --check-cfg 'cfg(name, values(any()))' +``` + +To avoid repeating the same set of values, use this form: + +```bash +rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))' ``` The `--check-cfg cfg(...)` option can be repeated, both for the same condition name and for different names. If it is repeated for the same condition name, then the sets of values for that -condition are merged together (presedence is given to `any()`). +condition are merged together (precedence is given to `values(any())`). ## Well known names and values `rustc` has a internal list of well known names and their corresponding values. Those well known names and values follows the same stability as what they refer to. -Well known values checking is always enabled as long as a `--check-cfg` argument is present. +Well known names and values checking is always enabled as long as at least one +`--check-cfg` argument is present. -Well known names checking is always enable as long as a `--check-cfg` argument is present -**unless** any `cfg(any())` argument is passed. +As of `2024-01-09T`, the list of known names is as follows: -To disable checking of well known names, use this form: + -```bash -rustc --check-cfg 'cfg(any())' -``` + - `debug_assertions` + - `doc` + - `doctest` + - `miri` + - `overflow_checks` + - `panic` + - `proc_macro` + - `relocation_model` + - `sanitize` + - `sanitizer_cfi_generalize_pointers` + - `sanitizer_cfi_normalize_integers` + - `target_abi` + - `target_arch` + - `target_endian` + - `target_env` + - `target_family` + - `target_feature` + - `target_has_atomic` + - `target_has_atomic_equal_alignment` + - `target_has_atomic_load_store` + - `target_os` + - `target_pointer_width` + - `target_thread_local` + - `target_vendor` + - `test` + - `unix` + - `windows` -NOTE: If one want to enable values and names checking without having any cfg to declare, one -can use an empty `cfg()` argument. +Like with `values(any())`, well known names checking can be disabled by passing `cfg(any())` +as argument to `--check-cfg`. ## Examples +### Equivalence table + +This table describe the equivalence of a `--cfg` argument to a `--check-cfg` argument. + +| `--cfg` | `--check-cfg` | +|-----------------------------|----------------------------------------------------------| +| *nothing* | *nothing* or `--check-cfg=cfg()` (to enable the checking) | +| `--cfg foo` | `--check-cfg=cfg(foo) or --check-cfg=cfg(foo, values())` | +| `--cfg foo=""` | `--check-cfg=cfg(foo, values(""))` | +| `--cfg foo="bar"` | `--check-cfg=cfg(foo, values("bar"))` | +| `--cfg foo="1" --cfg foo="2"` | `--check-cfg=cfg(foo, values("1", "2"))` | +| `--cfg foo="1" --cfg bar="2"` | `--check-cfg=cfg(foo, values("1")) --check-cfg=cfg(bar, values("2"))` | +| `--cfg foo --cfg foo="bar"` | `--check-cfg=cfg(foo) --check-cfg=cfg(foo, values("bar"))` | + +NOTE: There is (currently) no way to express that a condition name is expected but no (!= none) +values are expected. Passing an empty `values()` means *(none)* in the sense of `#[cfg(foo)]` +with no value. Users are expected to NOT pass a `--check-cfg` with that condition name. + +### Example: Cargo-like `feature` example + Consider this command line: ```bash rustc --check-cfg 'cfg(feature, values("lion", "zebra"))' \ - --cfg 'feature="lion"' -Z unstable-options \ - example.rs + --cfg 'feature="lion"' -Z unstable-options example.rs ``` This command line indicates that this crate has two features: `lion` and `zebra`. The `lion` -feature is enabled, while the `zebra` feature is disabled. Exhaustive checking of names and -values are enabled by default. Consider compiling this code: +feature is enabled, while the `zebra` feature is disabled. +Given the `--check-cfg` arguments, exhaustive checking of names and +values are enabled. +`example.rs`: ```rust -// This is expected, and tame_lion() will be compiled -#[cfg(feature = "lion")] +#[cfg(feature = "lion")] // This condition is expected, as "lion" is an expected value of `feature` fn tame_lion(lion: Lion) {} -// This is expected, and ride_zebra() will NOT be compiled. -#[cfg(feature = "zebra")] -fn ride_zebra(zebra: Zebra) {} +#[cfg(feature = "zebra")] // This condition is expected, as "zebra" is an expected value of `feature` + // but the condition will still evaluate to false + // since only --cfg feature="lion" was passed +fn ride_zebra(z: Zebra) {} -// This is UNEXPECTED, and will cause a compiler warning (by default). -#[cfg(feature = "platypus")] +#[cfg(feature = "platypus")] // This condition is UNEXPECTED, as "platypus" is NOT an expected value of + // `feature` and will cause a compiler warning (by default). fn poke_platypus() {} -// This is UNEXPECTED, because 'feechure' is not a known condition name, -// and will cause a compiler warning (by default). -#[cfg(feechure = "lion")] +#[cfg(feechure = "lion")] // This condition is UNEXPECTED, as 'feechure' is NOT a expected condition + // name, no `cfg(feechure, ...)` was passed in `--check-cfg` fn tame_lion() {} -// This is UNEXPECTED, because 'windows' is a well known condition name, -// and because 'windows' doesn't take any values, -// and will cause a compiler warning (by default). -#[cfg(windows = "unix")] +#[cfg(windows = "unix")] // This condition is UNEXPECTED, as while 'windows' is a well known + // condition name, it doens't expect any values fn tame_windows() {} ``` -### Example: Checking condition names, but not values +### Example: Multiple names and values ```bash -# This turns on checking for condition names, but not values, such as 'feature' values. -rustc --check-cfg 'cfg(is_embedded, has_feathers, values(any()))' \ - --cfg has_feathers -Z unstable-options -``` - -```rust -#[cfg(is_embedded)] // This is expected as "is_embedded" was provided in cfg() -fn do_embedded() {} // and because names exhaustiveness was not disabled - -#[cfg(has_feathers)] // This is expected as "has_feathers" was provided in cfg() -fn do_features() {} // and because names exhaustiveness was not disabled - -#[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in cfg() - // and because no value checking was enable for "has_feathers" - // no warning is emitted for the value "zapping" -fn do_zapping() {} - -#[cfg(has_mumble_frotz)] // This is UNEXPECTED because names checking is enable and - // "has_mumble_frotz" was not provided in cfg() -fn do_mumble_frotz() {} -``` - -### Example: Checking feature values, but not condition names - -```bash -# This turns on checking for feature values, but not for condition names. -rustc --check-cfg 'cfg(feature, values("zapping", "lasers"))' \ - --check-cfg 'cfg(any())' \ - --cfg 'feature="zapping"' -Z unstable-options -``` - -```rust -#[cfg(is_embedded)] // This is doesn't raise a warning, because names checking was - // disabled by 'cfg(any())' -fn do_embedded() {} - -#[cfg(has_feathers)] // Same as above, 'cfg(any())' was provided so no name - // checking is performed -fn do_features() {} - -#[cfg(feature = "lasers")] // This is expected, "lasers" is in the cfg(feature) list -fn shoot_lasers() {} - -#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in the - // cfg(feature) list -fn write_shakespeare() {} -``` - -### Example: Checking both condition names and feature values - -```bash -# This turns on checking for feature values and for condition names. rustc --check-cfg 'cfg(is_embedded, has_feathers)' \ --check-cfg 'cfg(feature, values("zapping", "lasers"))' \ --cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options ``` ```rust -#[cfg(is_embedded)] // This is expected because "is_embedded" was provided in cfg() +#[cfg(is_embedded)] // This condition is expected, as 'is_embedded' was provided in --check-cfg fn do_embedded() {} // and doesn't take any value -#[cfg(has_feathers)] // This is expected because "has_feathers" was provided in cfg() -fn do_features() {} // and deosn't take any value +#[cfg(has_feathers)] // This condition is expected, as 'has_feathers' was provided in --check-cfg +fn do_features() {} // and doesn't take any value -#[cfg(has_mumble_frotz)] // This is UNEXPECTED, because "has_mumble_frotz" was never provided +#[cfg(has_mumble_frotz)] // This condition is UNEXPECTED, as 'has_mumble_frotz' was NEVER provided + // in any --check-cfg arguments fn do_mumble_frotz() {} -#[cfg(feature = "lasers")] // This is expected, "lasers" is in the cfg(feature) list +#[cfg(feature = "lasers")] // This condition is expected, as "lasers" is an expected value of `feature` fn shoot_lasers() {} -#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in - // the cfg(feature) list +#[cfg(feature = "monkeys")] // This condition is UNEXPECTED, as "monkeys" is NOT an expected value of + // `feature` fn write_shakespeare() {} ``` + +### Example: Condition names without values + +```bash +rustc --check-cfg 'cfg(is_embedded, has_feathers, values(any()))' \ + --cfg has_feathers -Z unstable-options +``` + +```rust +#[cfg(is_embedded)] // This condition is expected, as 'is_embedded' was provided in --check-cfg + // as condition name +fn do_embedded() {} + +#[cfg(has_feathers)] // This condition is expected, as "has_feathers" was provided in --check-cfg + // as condition name +fn do_features() {} + +#[cfg(has_feathers = "zapping")] // This condition is expected, as "has_feathers" was provided in + // and because *any* values is expected for 'has_feathers' no + // warning is emitted for the value "zapping" +fn do_zapping() {} + +#[cfg(has_mumble_frotz)] // This condition is UNEXPECTED, as 'has_mumble_frotz' was not provided + // in any --check-cfg arguments +fn do_mumble_frotz() {} +``` From 18a1ca6a1779d3a0ad506666de8e3fdd03704d9c Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 9 Jan 2024 04:40:54 +0100 Subject: [PATCH 244/257] core: panic: fix broken link Signed-off-by: Miguel Ojeda --- library/core/src/macros/panic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/macros/panic.md b/library/core/src/macros/panic.md index 60100c2655a6..6bd23b3072ed 100644 --- a/library/core/src/macros/panic.md +++ b/library/core/src/macros/panic.md @@ -56,7 +56,7 @@ For more detailed information about error handling check out the [book] or the [`panic_any`]: ../std/panic/fn.panic_any.html [`Box`]: ../std/boxed/struct.Box.html [`Any`]: crate::any::Any -[`format!` syntax]: ../std/fmt/index.html +[formatting syntax]: ../std/fmt/index.html [book]: ../book/ch09-00-error-handling.html [`std::result`]: ../std/result/index.html From f141a524e8cf778756931ff8c776f4934eda1c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 8 Jan 2024 22:37:42 +0100 Subject: [PATCH 245/257] Rename rustc_middle path cleaning functions The new names are consistent with the other rustc_middle cleaning functions. Regarding the local variable `ty_args`, it's used throughout the function and personally speaking its name isn't very legible, I trip up on it. --- src/librustdoc/clean/mod.rs | 26 +++++++++++++-------- src/librustdoc/clean/types.rs | 4 ++-- src/librustdoc/clean/utils.rs | 44 +++++++++++++++-------------------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1a25d3f79936..520d60f5b9b1 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -207,8 +207,13 @@ pub(crate) fn clean_trait_ref_with_bindings<'tcx>( span_bug!(cx.tcx.def_span(trait_ref.def_id()), "`TraitRef` had unexpected kind {kind:?}"); } inline::record_extern_fqn(cx, trait_ref.def_id(), kind); - let path = - external_path(cx, trait_ref.def_id(), true, bindings, trait_ref.map_bound(|tr| tr.args)); + let path = clean_middle_path( + cx, + trait_ref.def_id(), + true, + bindings, + trait_ref.map_bound(|tr| tr.args), + ); debug!(?trait_ref); @@ -467,7 +472,7 @@ fn projection_to_path_segment<'tcx>( PathSegment { name: item.name, args: GenericArgs::AngleBracketed { - args: ty_args_to_args( + args: clean_middle_generic_args( cx, ty.map_bound(|ty| &ty.args[generics.parent_count..]), false, @@ -2096,12 +2101,12 @@ pub(crate) fn clean_middle_ty<'tcx>( AdtKind::Enum => ItemType::Enum, }; inline::record_extern_fqn(cx, did, kind); - let path = external_path(cx, did, false, ThinVec::new(), bound_ty.rebind(args)); + let path = clean_middle_path(cx, did, false, ThinVec::new(), bound_ty.rebind(args)); Type::Path { path } } ty::Foreign(did) => { inline::record_extern_fqn(cx, did, ItemType::ForeignType); - let path = external_path( + let path = clean_middle_path( cx, did, false, @@ -2132,7 +2137,7 @@ pub(crate) fn clean_middle_ty<'tcx>( let mut bounds = dids .map(|did| { let empty = ty::Binder::dummy(ty::GenericArgs::empty()); - let path = external_path(cx, did, false, ThinVec::new(), empty); + let path = clean_middle_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); PolyTrait { trait_: path, generic_params: Vec::new() } }) @@ -2171,7 +2176,7 @@ pub(crate) fn clean_middle_ty<'tcx>( .collect(); let late_bound_regions = late_bound_regions.into_iter().collect(); - let path = external_path(cx, did, false, bindings, args); + let path = clean_middle_path(cx, did, false, bindings, args); bounds.insert(0, PolyTrait { trait_: path, generic_params: late_bound_regions }); DynTrait(bounds, lifetime) @@ -2193,7 +2198,7 @@ pub(crate) fn clean_middle_ty<'tcx>( assoc: PathSegment { name: cx.tcx.associated_item(def_id).name, args: GenericArgs::AngleBracketed { - args: ty_args_to_args( + args: clean_middle_generic_args( cx, alias_ty.map_bound(|ty| ty.args.as_slice()), true, @@ -2213,7 +2218,7 @@ pub(crate) fn clean_middle_ty<'tcx>( if cx.tcx.features().lazy_type_alias { // Weak type alias `data` represents the `type X` in `type X = Y`. If we need `Y`, // we need to use `type_of`. - let path = external_path( + let path = clean_middle_path( cx, data.def_id, false, @@ -2243,7 +2248,8 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { // If it's already in the same alias, don't get an infinite loop. if cx.current_type_aliases.contains_key(&def_id) { - let path = external_path(cx, def_id, false, ThinVec::new(), bound_ty.rebind(args)); + let path = + clean_middle_path(cx, def_id, false, ThinVec::new(), bound_ty.rebind(args)); Type::Path { path } } else { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 179f37e6d96a..90eb783c7cae 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -36,7 +36,7 @@ use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use crate::clean::cfg::Cfg; -use crate::clean::external_path; +use crate::clean::clean_middle_path; use crate::clean::inline::{self, print_inlined_const}; use crate::clean::utils::{is_literal_expr, print_evaluated_const}; use crate::core::DocContext; @@ -1258,7 +1258,7 @@ impl GenericBound { fn sized_with(cx: &mut DocContext<'_>, modifier: hir::TraitBoundModifier) -> GenericBound { let did = cx.tcx.require_lang_item(LangItem::Sized, None); let empty = ty::Binder::dummy(ty::GenericArgs::empty()); - let path = external_path(cx, did, false, ThinVec::new(), empty); + let path = clean_middle_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); GenericBound::TraitBound(PolyTrait { trait_: path, generic_params: Vec::new() }, modifier) } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index bdfda07be096..cb5cfef63822 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -75,13 +75,13 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { Crate { module, external_traits: cx.external_traits.clone() } } -pub(crate) fn ty_args_to_args<'tcx>( +pub(crate) fn clean_middle_generic_args<'tcx>( cx: &mut DocContext<'tcx>, - ty_args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, + args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, has_self: bool, owner: DefId, ) -> Vec { - if ty_args.skip_binder().is_empty() { + if args.skip_binder().is_empty() { // Fast path which avoids executing the query `generics_of`. return Vec::new(); } @@ -90,9 +90,9 @@ pub(crate) fn ty_args_to_args<'tcx>( let mut elision_has_failed_once_before = false; let offset = if has_self { 1 } else { 0 }; - let mut args = Vec::with_capacity(ty_args.skip_binder().len().saturating_sub(offset)); + let mut clean_args = Vec::with_capacity(args.skip_binder().len().saturating_sub(offset)); - let ty_arg_to_arg = |(index, arg): (usize, &ty::GenericArg<'tcx>)| match arg.unpack() { + let clean_arg = |(index, arg): (usize, &ty::GenericArg<'tcx>)| match arg.unpack() { GenericArgKind::Lifetime(lt) => { Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) } @@ -101,10 +101,9 @@ pub(crate) fn ty_args_to_args<'tcx>( if !elision_has_failed_once_before && let Some(default) = params[index].default_value(cx.tcx) { - let default = - ty_args.map_bound(|args| default.instantiate(cx.tcx, args).expect_ty()); + let default = args.map_bound(|args| default.instantiate(cx.tcx, args).expect_ty()); - if can_elide_generic_arg(ty_args.rebind(ty), default) { + if can_elide_generic_arg(args.rebind(ty), default) { return None; } @@ -112,15 +111,10 @@ pub(crate) fn ty_args_to_args<'tcx>( } Some(GenericArg::Type(clean_middle_ty( - ty_args.rebind(ty), + args.rebind(ty), cx, None, - Some(crate::clean::ContainerTy::Regular { - ty: owner, - args: ty_args, - has_self, - arg: index, - }), + Some(crate::clean::ContainerTy::Regular { ty: owner, args, has_self, arg: index }), ))) } GenericArgKind::Const(ct) => { @@ -133,22 +127,22 @@ pub(crate) fn ty_args_to_args<'tcx>( && let Some(default) = params[index].default_value(cx.tcx) { let default = - ty_args.map_bound(|args| default.instantiate(cx.tcx, args).expect_const()); + args.map_bound(|args| default.instantiate(cx.tcx, args).expect_const()); - if can_elide_generic_arg(ty_args.rebind(ct), default) { + if can_elide_generic_arg(args.rebind(ct), default) { return None; } elision_has_failed_once_before = true; } - Some(GenericArg::Const(Box::new(clean_middle_const(ty_args.rebind(ct), cx)))) + Some(GenericArg::Const(Box::new(clean_middle_const(args.rebind(ct), cx)))) } }; - args.extend(ty_args.skip_binder().iter().enumerate().rev().filter_map(ty_arg_to_arg)); - args.reverse(); - args + clean_args.extend(args.skip_binder().iter().enumerate().rev().filter_map(clean_arg)); + clean_args.reverse(); + clean_args } /// Check if the generic argument `actual` coincides with the `default` and can therefore be elided. @@ -192,14 +186,14 @@ where actual.skip_binder() == default.skip_binder() } -fn external_generic_args<'tcx>( +fn clean_middle_generic_args_with_bindings<'tcx>( cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, bindings: ThinVec, ty_args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, ) -> GenericArgs { - let args = ty_args_to_args(cx, ty_args.map_bound(|args| &args[..]), has_self, did); + let args = clean_middle_generic_args(cx, ty_args.map_bound(|args| &args[..]), has_self, did); if cx.tcx.fn_trait_kind_from_def_id(did).is_some() { let ty = ty_args @@ -225,7 +219,7 @@ fn external_generic_args<'tcx>( } } -pub(super) fn external_path<'tcx>( +pub(super) fn clean_middle_path<'tcx>( cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, @@ -238,7 +232,7 @@ pub(super) fn external_path<'tcx>( res: Res::Def(def_kind, did), segments: thin_vec![PathSegment { name, - args: external_generic_args(cx, did, has_self, bindings, args), + args: clean_middle_generic_args_with_bindings(cx, did, has_self, bindings, args), }], } } From 5b32681db1704d99cb9aef11903362c70c2ac988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 8 Jan 2024 23:28:30 +0100 Subject: [PATCH 246/257] Offset args of trait object types when cleaning --- src/librustdoc/clean/mod.rs | 14 ++++---------- src/librustdoc/clean/utils.rs | 31 +++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 520d60f5b9b1..9cc2d9cecb73 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1988,8 +1988,9 @@ pub(crate) enum ContainerTy<'tcx> { Ref(ty::Region<'tcx>), Regular { ty: DefId, + /// The arguments *have* to contain an arg for the self type if the corresponding generics + /// contain a self type. args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, - has_self: bool, arg: usize, }, } @@ -1998,7 +1999,7 @@ impl<'tcx> ContainerTy<'tcx> { fn object_lifetime_default(self, tcx: TyCtxt<'tcx>) -> ObjectLifetimeDefault<'tcx> { match self { Self::Ref(region) => ObjectLifetimeDefault::Arg(region), - Self::Regular { ty: container, args, has_self, arg: index } => { + Self::Regular { ty: container, args, arg: index } => { let (DefKind::Struct | DefKind::Union | DefKind::Enum @@ -2011,14 +2012,7 @@ impl<'tcx> ContainerTy<'tcx> { let generics = tcx.generics_of(container); debug_assert_eq!(generics.parent_count, 0); - // If the container is a trait object type, the arguments won't contain the self type but the - // generics of the corresponding trait will. In such a case, offset the index by one. - // For comparison, if the container is a trait inside a bound, the arguments do contain the - // self type. - let offset = - if !has_self && generics.parent.is_none() && generics.has_self { 1 } else { 0 }; - let param = generics.params[index + offset].def_id; - + let param = generics.params[index].def_id; let default = tcx.object_lifetime_default(param); match default { rbv::ObjectLifetimeDefault::Param(lifetime) => { diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index cb5cfef63822..aadc308d5ecb 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -78,7 +78,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { pub(crate) fn clean_middle_generic_args<'tcx>( cx: &mut DocContext<'tcx>, args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, - has_self: bool, + mut has_self: bool, owner: DefId, ) -> Vec { if args.skip_binder().is_empty() { @@ -86,12 +86,30 @@ pub(crate) fn clean_middle_generic_args<'tcx>( return Vec::new(); } - let params = &cx.tcx.generics_of(owner).params; + let generics = cx.tcx.generics_of(owner); let mut elision_has_failed_once_before = false; let offset = if has_self { 1 } else { 0 }; let mut clean_args = Vec::with_capacity(args.skip_binder().len().saturating_sub(offset)); + // If the container is a trait object type, the arguments won't contain the self type but the + // generics of the corresponding trait will. In such a case, prepend a dummy self type in order + // to align the arguments and parameters for the iteration below and to enable us to correctly + // instantiate the generic parameter default later. + let args = if !has_self && generics.parent.is_none() && generics.has_self { + has_self = true; + // FIXME(fmease): Don't arena-allocate the args (blocked on further refactorings)! + args.map_bound(|args| { + &*cx.tcx.arena.alloc_from_iter( + [cx.tcx.types.trait_object_dummy_self.into()] + .into_iter() + .chain(args.iter().copied()), + ) + }) + } else { + args + }; + let clean_arg = |(index, arg): (usize, &ty::GenericArg<'tcx>)| match arg.unpack() { GenericArgKind::Lifetime(lt) => { Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) @@ -99,7 +117,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( GenericArgKind::Type(_) if has_self && index == 0 => None, GenericArgKind::Type(ty) => { if !elision_has_failed_once_before - && let Some(default) = params[index].default_value(cx.tcx) + && let Some(default) = generics.param_at(index, cx.tcx).default_value(cx.tcx) { let default = args.map_bound(|args| default.instantiate(cx.tcx, args).expect_ty()); @@ -114,17 +132,18 @@ pub(crate) fn clean_middle_generic_args<'tcx>( args.rebind(ty), cx, None, - Some(crate::clean::ContainerTy::Regular { ty: owner, args, has_self, arg: index }), + Some(crate::clean::ContainerTy::Regular { ty: owner, args, arg: index }), ))) } GenericArgKind::Const(ct) => { - if let ty::GenericParamDefKind::Const { is_host_effect: true, .. } = params[index].kind + if let ty::GenericParamDefKind::Const { is_host_effect: true, .. } = + generics.param_at(index, cx.tcx).kind { return None; } if !elision_has_failed_once_before - && let Some(default) = params[index].default_value(cx.tcx) + && let Some(default) = generics.param_at(index, cx.tcx).default_value(cx.tcx) { let default = args.map_bound(|args| default.instantiate(cx.tcx, args).expect_const()); From 17ec134fa421dd152df12063e45afe355e585d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 8 Jan 2024 22:31:50 +0100 Subject: [PATCH 247/257] Update tests --- .../auxiliary/default-generic-args.rs | 9 ++++-- .../auxiliary/u_default_generic_args.rs | 1 + .../inline_cross/default-generic-args.rs | 32 ++++++++++++------- 3 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 tests/rustdoc/inline_cross/auxiliary/u_default_generic_args.rs diff --git a/tests/rustdoc/inline_cross/auxiliary/default-generic-args.rs b/tests/rustdoc/inline_cross/auxiliary/default-generic-args.rs index 1e31f18927e9..c2c94817fad3 100644 --- a/tests/rustdoc/inline_cross/auxiliary/default-generic-args.rs +++ b/tests/rustdoc/inline_cross/auxiliary/default-generic-args.rs @@ -40,6 +40,11 @@ pub struct Multi(A, B); pub type M0 = Multi; -pub trait Trait<'a, T = &'a ()> {} +pub trait Trait0<'a, T = &'a ()> {} +pub type D0 = dyn for<'a> Trait0<'a>; -pub type F = dyn for<'a> Trait<'a>; +// Regression test for issue #119529. +pub trait Trait1 {} +pub type D1 = dyn Trait1; +pub type D2 = dyn Trait1<(), K>; +pub type D3 = dyn Trait1; diff --git a/tests/rustdoc/inline_cross/auxiliary/u_default_generic_args.rs b/tests/rustdoc/inline_cross/auxiliary/u_default_generic_args.rs new file mode 100644 index 000000000000..a742dd7d865f --- /dev/null +++ b/tests/rustdoc/inline_cross/auxiliary/u_default_generic_args.rs @@ -0,0 +1 @@ +pub use default_generic_args::*; diff --git a/tests/rustdoc/inline_cross/default-generic-args.rs b/tests/rustdoc/inline_cross/default-generic-args.rs index c9a87a199018..775bf0415328 100644 --- a/tests/rustdoc/inline_cross/default-generic-args.rs +++ b/tests/rustdoc/inline_cross/default-generic-args.rs @@ -79,15 +79,14 @@ pub use default_generic_args::P1; pub use default_generic_args::P2; // @has user/type.A0.html -// Ensure that we elide generic arguments that are alpha-equivalent to their respective -// generic parameter (modulo substs) (#1): -// @has - '//*[@class="rust item-decl"]//code' "Alpha" +// @has - '//*[@class="rust item-decl"]//code' "Alpha;" pub use default_generic_args::A0; // @has user/type.A1.html -// Ensure that we elide generic arguments that are alpha-equivalent to their respective -// generic parameter (modulo substs) (#1): -// @has - '//*[@class="rust item-decl"]//code' "Alpha" +// Demonstrates that we currently don't elide generic arguments that are alpha-equivalent to their +// respective generic parameter (after instantiation) for perf reasons (it would require us to +// create an inference context). +// @has - '//*[@class="rust item-decl"]//code' "Alpha fn(_: &'arbitrary ())>" pub use default_generic_args::A1; // @has user/type.M0.html @@ -97,8 +96,19 @@ pub use default_generic_args::A1; // @has - '//*[@class="rust item-decl"]//code' "Multi" pub use default_generic_args::M0; -// @has user/type.F.html -// FIXME: Ideally, we would elide `&'a ()` but `'a` is an escaping bound var which we can't reason -// about at the moment since we don't keep track of bound vars. -// @has - '//*[@class="rust item-decl"]//code' "dyn for<'a> Trait<'a, &'a ()>" -pub use default_generic_args::F; +// @has user/type.D0.html +// @has - '//*[@class="rust item-decl"]//code' "dyn for<'a> Trait0<'a>" +pub use default_generic_args::D0; + +// Regression test for issue #119529. +// Check that we correctly elide def ty&const args inside trait object types. + +// @has user/type.D1.html +// @has - '//*[@class="rust item-decl"]//code' "dyn Trait1" +pub use default_generic_args::D1; +// @has user/type.D2.html +// @has - '//*[@class="rust item-decl"]//code' "dyn Trait1<(), K>" +pub use default_generic_args::D2; +// @has user/type.D3.html +// @has - '//*[@class="rust item-decl"]//code' "dyn Trait1;" +pub use default_generic_args::D3; From 4f0869ea8903947d61b6db73178a4234a9959feb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 9 Jan 2024 14:46:30 +0000 Subject: [PATCH 248/257] Fix an ICE that occurs after an error has already been reported --- compiler/rustc_transmute/src/layout/tree.rs | 1 + .../transmute_infinitely_recursive_type.rs | 26 +++++++++++++++++++ ...transmute_infinitely_recursive_type.stderr | 21 +++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs create mode 100644 tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 49f24f66b24a..86a077ee808b 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -199,6 +199,7 @@ pub(crate) mod rustc { match err { LayoutError::Unknown(..) | LayoutError::ReferencesError(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, + LayoutError::Cycle(err) => Self::TypeError(*err), err => unimplemented!("{:?}", err), } } diff --git a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs new file mode 100644 index 000000000000..0be5b41c80bc --- /dev/null +++ b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs @@ -0,0 +1,26 @@ +//~ ERROR: cycle detected +//! Safe transmute did not handle cycle errors that could occur during +//! layout computation. This test checks that we do not ICE in such +//! situations (see #117491). +#![crate_type = "lib"] +#![feature(transmutability)] +#![allow(dead_code, incomplete_features, non_camel_case_types)] + +mod assert { + use std::mem::{Assume, BikeshedIntrinsicFrom}; + pub struct Context; + + pub fn is_maybe_transmutable() + where + Dst: BikeshedIntrinsicFrom, + { + } +} + +fn should_pad_explicitly_packed_field() { + #[repr(C)] + struct ExplicitlyPadded(ExplicitlyPadded); + //~^ ERROR: recursive type + + assert::is_maybe_transmutable::(); +} diff --git a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr new file mode 100644 index 000000000000..0dedd5aaf735 --- /dev/null +++ b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr @@ -0,0 +1,21 @@ +error[E0072]: recursive type `ExplicitlyPadded` has infinite size + --> $DIR/transmute_infinitely_recursive_type.rs:22:5 + | +LL | struct ExplicitlyPadded(ExplicitlyPadded); + | ^^^^^^^^^^^^^^^^^^^^^^^ ---------------- recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle + | +LL | struct ExplicitlyPadded(Box); + | ++++ + + +error[E0391]: cycle detected when computing layout of `should_pad_explicitly_packed_field::ExplicitlyPadded` + | + = note: ...which immediately requires computing layout of `should_pad_explicitly_packed_field::ExplicitlyPadded` again + = note: cycle used when evaluating trait selection obligation `(): core::mem::transmutability::BikeshedIntrinsicFrom` + = 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: aborting due to 2 previous errors + +Some errors have detailed explanations: E0072, E0391. +For more information about an error, try `rustc --explain E0072`. From 4b859e68aa9454612a22c65f91c100fc7c920af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 9 Jan 2024 13:56:28 +0100 Subject: [PATCH 249/257] Don't arena-allocate extended generic args --- src/librustdoc/clean/mod.rs | 12 ++++----- src/librustdoc/clean/utils.rs | 47 +++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 9cc2d9cecb73..ab5cf5499057 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1908,7 +1908,7 @@ fn normalize<'tcx>( fn clean_trait_object_lifetime_bound<'tcx>( region: ty::Region<'tcx>, - container: Option>, + container: Option>, preds: &'tcx ty::List>, tcx: TyCtxt<'tcx>, ) -> Option { @@ -1937,7 +1937,7 @@ fn clean_trait_object_lifetime_bound<'tcx>( fn can_elide_trait_object_lifetime_bound<'tcx>( region: ty::Region<'tcx>, - container: Option>, + container: Option>, preds: &'tcx ty::List>, tcx: TyCtxt<'tcx>, ) -> bool { @@ -1984,18 +1984,18 @@ fn can_elide_trait_object_lifetime_bound<'tcx>( } #[derive(Debug)] -pub(crate) enum ContainerTy<'tcx> { +pub(crate) enum ContainerTy<'a, 'tcx> { Ref(ty::Region<'tcx>), Regular { ty: DefId, /// The arguments *have* to contain an arg for the self type if the corresponding generics /// contain a self type. - args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, + args: ty::Binder<'tcx, &'a [ty::GenericArg<'tcx>]>, arg: usize, }, } -impl<'tcx> ContainerTy<'tcx> { +impl<'tcx> ContainerTy<'_, 'tcx> { fn object_lifetime_default(self, tcx: TyCtxt<'tcx>) -> ObjectLifetimeDefault<'tcx> { match self { Self::Ref(region) => ObjectLifetimeDefault::Arg(region), @@ -2044,7 +2044,7 @@ pub(crate) fn clean_middle_ty<'tcx>( bound_ty: ty::Binder<'tcx, Ty<'tcx>>, cx: &mut DocContext<'tcx>, parent_def_id: Option, - container: Option>, + container: Option>, ) -> Type { let bound_ty = normalize(cx, bound_ty).unwrap_or(bound_ty); match *bound_ty.skip_binder().kind() { diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index aadc308d5ecb..e957cf1f2f52 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -81,7 +81,8 @@ pub(crate) fn clean_middle_generic_args<'tcx>( mut has_self: bool, owner: DefId, ) -> Vec { - if args.skip_binder().is_empty() { + let (args, bound_vars) = (args.skip_binder(), args.bound_vars()); + if args.is_empty() { // Fast path which avoids executing the query `generics_of`. return Vec::new(); } @@ -90,7 +91,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( let mut elision_has_failed_once_before = false; let offset = if has_self { 1 } else { 0 }; - let mut clean_args = Vec::with_capacity(args.skip_binder().len().saturating_sub(offset)); + let mut clean_args = Vec::with_capacity(args.len().saturating_sub(offset)); // If the container is a trait object type, the arguments won't contain the self type but the // generics of the corresponding trait will. In such a case, prepend a dummy self type in order @@ -98,16 +99,13 @@ pub(crate) fn clean_middle_generic_args<'tcx>( // instantiate the generic parameter default later. let args = if !has_self && generics.parent.is_none() && generics.has_self { has_self = true; - // FIXME(fmease): Don't arena-allocate the args (blocked on further refactorings)! - args.map_bound(|args| { - &*cx.tcx.arena.alloc_from_iter( - [cx.tcx.types.trait_object_dummy_self.into()] - .into_iter() - .chain(args.iter().copied()), - ) - }) + [cx.tcx.types.trait_object_dummy_self.into()] + .into_iter() + .chain(args.iter().copied()) + .collect::>() + .into() } else { - args + std::borrow::Cow::from(args) }; let clean_arg = |(index, arg): (usize, &ty::GenericArg<'tcx>)| match arg.unpack() { @@ -116,12 +114,13 @@ pub(crate) fn clean_middle_generic_args<'tcx>( } GenericArgKind::Type(_) if has_self && index == 0 => None, GenericArgKind::Type(ty) => { + let ty = ty::Binder::bind_with_vars(ty, bound_vars); + if !elision_has_failed_once_before && let Some(default) = generics.param_at(index, cx.tcx).default_value(cx.tcx) { - let default = args.map_bound(|args| default.instantiate(cx.tcx, args).expect_ty()); - - if can_elide_generic_arg(args.rebind(ty), default) { + let default = default.instantiate(cx.tcx, args.as_ref()).expect_ty(); + if can_elide_generic_arg(ty, ty.rebind(default)) { return None; } @@ -129,10 +128,14 @@ pub(crate) fn clean_middle_generic_args<'tcx>( } Some(GenericArg::Type(clean_middle_ty( - args.rebind(ty), + ty, cx, None, - Some(crate::clean::ContainerTy::Regular { ty: owner, args, arg: index }), + Some(crate::clean::ContainerTy::Regular { + ty: owner, + args: ty.rebind(args.as_ref()), + arg: index, + }), ))) } GenericArgKind::Const(ct) => { @@ -142,24 +145,24 @@ pub(crate) fn clean_middle_generic_args<'tcx>( return None; } + let ct = ty::Binder::bind_with_vars(ct, bound_vars); + if !elision_has_failed_once_before && let Some(default) = generics.param_at(index, cx.tcx).default_value(cx.tcx) { - let default = - args.map_bound(|args| default.instantiate(cx.tcx, args).expect_const()); - - if can_elide_generic_arg(args.rebind(ct), default) { + let default = default.instantiate(cx.tcx, args.as_ref()).expect_const(); + if can_elide_generic_arg(ct, ct.rebind(default)) { return None; } elision_has_failed_once_before = true; } - Some(GenericArg::Const(Box::new(clean_middle_const(args.rebind(ct), cx)))) + Some(GenericArg::Const(Box::new(clean_middle_const(ct, cx)))) } }; - clean_args.extend(args.skip_binder().iter().enumerate().rev().filter_map(clean_arg)); + clean_args.extend(args.iter().enumerate().rev().filter_map(clean_arg)); clean_args.reverse(); clean_args } From aa1a5e3b6f0cc1711937b5ee29ac22eedb75c797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 9 Jan 2024 14:18:41 +0100 Subject: [PATCH 250/257] Simplify elision of default generic arguments --- src/librustdoc/clean/utils.rs | 88 ++++++++++++++++------------------- 1 file changed, 41 insertions(+), 47 deletions(-) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index e957cf1f2f52..0e4b00b09763 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -16,9 +16,10 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_metadata::rendered_const; use rustc_middle::mir; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt}; -use rustc_middle::ty::{TypeVisitable, TypeVisitableExt}; use rustc_span::symbol::{kw, sym, Symbol}; +use std::assert_matches::debug_assert_matches; use std::fmt::Write as _; use std::mem; use std::sync::LazyLock as Lazy; @@ -108,57 +109,46 @@ pub(crate) fn clean_middle_generic_args<'tcx>( std::borrow::Cow::from(args) }; - let clean_arg = |(index, arg): (usize, &ty::GenericArg<'tcx>)| match arg.unpack() { - GenericArgKind::Lifetime(lt) => { - Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) + let clean_arg = |(index, &arg): (usize, &ty::GenericArg<'tcx>)| { + // Elide the self type. + if has_self && index == 0 { + return None; } - GenericArgKind::Type(_) if has_self && index == 0 => None, - GenericArgKind::Type(ty) => { - let ty = ty::Binder::bind_with_vars(ty, bound_vars); - if !elision_has_failed_once_before - && let Some(default) = generics.param_at(index, cx.tcx).default_value(cx.tcx) - { - let default = default.instantiate(cx.tcx, args.as_ref()).expect_ty(); - if can_elide_generic_arg(ty, ty.rebind(default)) { - return None; - } + // Elide internal host effect args. + let param = generics.param_at(index, cx.tcx); + if param.is_host_effect() { + return None; + } - elision_has_failed_once_before = true; + let arg = ty::Binder::bind_with_vars(arg, bound_vars); + + // Elide arguments that coincide with their default. + if !elision_has_failed_once_before && let Some(default) = param.default_value(cx.tcx) { + let default = default.instantiate(cx.tcx, args.as_ref()); + if can_elide_generic_arg(arg, arg.rebind(default)) { + return None; } + elision_has_failed_once_before = true; + } - Some(GenericArg::Type(clean_middle_ty( - ty, + match arg.skip_binder().unpack() { + GenericArgKind::Lifetime(lt) => { + Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) + } + GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty( + arg.rebind(ty), cx, None, Some(crate::clean::ContainerTy::Regular { ty: owner, - args: ty.rebind(args.as_ref()), + args: arg.rebind(args.as_ref()), arg: index, }), - ))) - } - GenericArgKind::Const(ct) => { - if let ty::GenericParamDefKind::Const { is_host_effect: true, .. } = - generics.param_at(index, cx.tcx).kind - { - return None; + ))), + GenericArgKind::Const(ct) => { + Some(GenericArg::Const(Box::new(clean_middle_const(arg.rebind(ct), cx)))) } - - let ct = ty::Binder::bind_with_vars(ct, bound_vars); - - if !elision_has_failed_once_before - && let Some(default) = generics.param_at(index, cx.tcx).default_value(cx.tcx) - { - let default = default.instantiate(cx.tcx, args.as_ref()).expect_const(); - if can_elide_generic_arg(ct, ct.rebind(default)) { - return None; - } - - elision_has_failed_once_before = true; - } - - Some(GenericArg::Const(Box::new(clean_middle_const(ct, cx)))) } }; @@ -172,13 +162,17 @@ pub(crate) fn clean_middle_generic_args<'tcx>( /// This uses a very conservative approach for performance and correctness reasons, meaning for /// several classes of terms it claims that they cannot be elided even if they theoretically could. /// This is absolutely fine since it mostly concerns edge cases. -fn can_elide_generic_arg<'tcx, Term>( - actual: ty::Binder<'tcx, Term>, - default: ty::Binder<'tcx, Term>, -) -> bool -where - Term: Eq + TypeVisitable>, -{ +fn can_elide_generic_arg<'tcx>( + actual: ty::Binder<'tcx, ty::GenericArg<'tcx>>, + default: ty::Binder<'tcx, ty::GenericArg<'tcx>>, +) -> bool { + debug_assert_matches!( + (actual.skip_binder().unpack(), default.skip_binder().unpack()), + (ty::GenericArgKind::Lifetime(_), ty::GenericArgKind::Lifetime(_)) + | (ty::GenericArgKind::Type(_), ty::GenericArgKind::Type(_)) + | (ty::GenericArgKind::Const(_), ty::GenericArgKind::Const(_)) + ); + // In practice, we shouldn't have any inference variables at this point. // However to be safe, we bail out if we do happen to stumble upon them. if actual.has_infer() || default.has_infer() { From 2d010bc634ea1c8ad0005db2a5c9791fdc5c2454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 9 Jan 2024 14:28:35 +0100 Subject: [PATCH 251/257] Move variables closer to their usage sites --- src/librustdoc/clean/utils.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 0e4b00b09763..437517598ac4 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -88,16 +88,11 @@ pub(crate) fn clean_middle_generic_args<'tcx>( return Vec::new(); } - let generics = cx.tcx.generics_of(owner); - let mut elision_has_failed_once_before = false; - - let offset = if has_self { 1 } else { 0 }; - let mut clean_args = Vec::with_capacity(args.len().saturating_sub(offset)); - // If the container is a trait object type, the arguments won't contain the self type but the // generics of the corresponding trait will. In such a case, prepend a dummy self type in order // to align the arguments and parameters for the iteration below and to enable us to correctly // instantiate the generic parameter default later. + let generics = cx.tcx.generics_of(owner); let args = if !has_self && generics.parent.is_none() && generics.has_self { has_self = true; [cx.tcx.types.trait_object_dummy_self.into()] @@ -109,6 +104,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( std::borrow::Cow::from(args) }; + let mut elision_has_failed_once_before = false; let clean_arg = |(index, &arg): (usize, &ty::GenericArg<'tcx>)| { // Elide the self type. if has_self && index == 0 { @@ -152,6 +148,8 @@ pub(crate) fn clean_middle_generic_args<'tcx>( } }; + let offset = if has_self { 1 } else { 0 }; + let mut clean_args = Vec::with_capacity(args.len().saturating_sub(offset)); clean_args.extend(args.iter().enumerate().rev().filter_map(clean_arg)); clean_args.reverse(); clean_args From f1b8b7d7ae190a9cfc14a9c6ac5e4f78445ade46 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Fri, 26 May 2023 13:49:01 +0000 Subject: [PATCH 252/257] Add error code for missing base expression in struct update syntax --- compiler/rustc_ast_lowering/messages.ftl | 2 +- compiler/rustc_ast_lowering/src/errors.rs | 4 +-- compiler/rustc_error_codes/src/error_codes.rs | 1 + .../src/error_codes/E0797.md | 26 +++++++++++++++++++ .../struct_destructure_fail.stderr | 11 +++++--- 5 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 compiler/rustc_error_codes/src/error_codes/E0797.md diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index f4e3086f2b58..e7177402db1d 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -35,7 +35,7 @@ ast_lowering_bad_return_type_notation_output = ast_lowering_base_expression_double_dot = base expression required after `..` - .label = add a base expression here + .suggestion = add a base expression here ast_lowering_clobber_abi_not_supported = `clobber_abi` is not supported on this target diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index faa22eece380..2811fe104cd0 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -114,10 +114,10 @@ pub struct UnderscoreExprLhsAssign { } #[derive(Diagnostic, Clone, Copy)] -#[diag(ast_lowering_base_expression_double_dot)] +#[diag(ast_lowering_base_expression_double_dot, code = "E0797")] pub struct BaseExpressionDoubleDot { #[primary_span] - #[label] + #[suggestion(code = "/* expr */", applicability = "has-placeholders", style = "verbose")] pub span: Span, } diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index a1391cceb712..9cd9ed54d414 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -516,6 +516,7 @@ E0793: include_str!("./error_codes/E0793.md"), E0794: include_str!("./error_codes/E0794.md"), E0795: include_str!("./error_codes/E0795.md"), E0796: include_str!("./error_codes/E0796.md"), +E0797: include_str!("./error_codes/E0797.md"), } // Undocumented removed error codes. Note that many removed error codes are kept in the list above diff --git a/compiler/rustc_error_codes/src/error_codes/E0797.md b/compiler/rustc_error_codes/src/error_codes/E0797.md new file mode 100644 index 000000000000..8a912307264e --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0797.md @@ -0,0 +1,26 @@ +Struct update syntax was used without a base expression. + +Erroneous code example: + +```compile_fail,E0797 +struct Foo { + fizz: u8, + buzz: u8 +} + +let f1 = Foo { fizz: 10, buzz: 1}; +let f2 = Foo { fizz: 10, .. }; // error +``` + +Using struct update syntax requires a 'base expression'. +This will be used to fill remaining fields. + +``` +struct Foo { + fizz: u8, + buzz: u8 +} + +let f1 = Foo { fizz: 10, buzz: 1}; +let f2 = Foo { fizz: 10, ..f1 }; +``` diff --git a/tests/ui/destructuring-assignment/struct_destructure_fail.stderr b/tests/ui/destructuring-assignment/struct_destructure_fail.stderr index ae7b3d1e5a92..57851ed417ea 100644 --- a/tests/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/tests/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -12,11 +12,16 @@ error: functional record updates are not allowed in destructuring assignments LL | Struct { a, ..d } = Struct { a: 1, b: 2 }; | ^ help: consider removing the trailing pattern -error: base expression required after `..` +error[E0797]: base expression required after `..` --> $DIR/struct_destructure_fail.rs:15:19 | LL | Struct { a, .. }; - | ^ add a base expression here + | ^ + | +help: add a base expression here + | +LL | Struct { a, ../* expr */ }; + | ++++++++++ error[E0026]: struct `Struct` does not have a field named `c` --> $DIR/struct_destructure_fail.rs:10:20 @@ -41,5 +46,5 @@ LL | Struct { a, .. } = Struct { a: 1, b: 2 }; error: aborting due to 5 previous errors -Some errors have detailed explanations: E0026, E0027. +Some errors have detailed explanations: E0026, E0027, E0797. For more information about an error, try `rustc --explain E0026`. From 0978f6e010e0eb3dddfa2a1a374ae5567e1f7f4c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 31 Oct 2023 13:45:26 +0000 Subject: [PATCH 253/257] Avoid silencing relevant follow-up errors --- compiler/rustc_hir_analysis/src/lib.rs | 11 +- .../rustc_trait_selection/src/traits/mod.rs | 6 + .../issue-109071.no_gate.stderr | 4 +- .../associated-inherent-types/issue-109071.rs | 4 +- .../issue-109071.with_gate.stderr | 18 +- .../issue-109299-1.rs | 1 + .../issue-109299-1.stderr | 10 +- .../associated-inherent-types/issue-109768.rs | 1 + .../issue-109768.stderr | 24 +- tests/ui/associated-type-bounds/duplicate.rs | 12 + .../associated-type-bounds/duplicate.stderr | 214 ++++++++++++++---- ...on-ambig-between-bound-and-where-clause.rs | 2 +- ...pe-projection-from-multiple-supertraits.rs | 4 +- ...rojection-from-multiple-supertraits.stderr | 24 +- tests/ui/associated-types/issue-23595-1.rs | 1 + .../ui/associated-types/issue-23595-1.stderr | 18 +- tests/ui/async-await/issues/issue-65159.rs | 2 +- .../ui/async-await/issues/issue-65159.stderr | 16 +- .../rtn-in-impl-signature.rs | 1 + .../rtn-in-impl-signature.stderr | 14 +- .../async-closure-gate.afn.stderr | 44 +++- .../async-closure-gate.nofeat.stderr | 44 +++- .../track-caller/async-closure-gate.rs | 2 + .../issue-82126-mismatched-subst-and-hir.rs | 1 + ...ssue-82126-mismatched-subst-and-hir.stderr | 18 +- .../assoc_const_eq_diagnostic.rs | 3 +- .../assoc_const_eq_diagnostic.stderr | 23 +- .../generic_const_exprs/issue-102768.rs | 5 + .../generic_const_exprs/issue-102768.stderr | 84 ++++++- .../generic_const_exprs/issue-105257.rs | 1 + .../generic_const_exprs/issue-105257.stderr | 11 +- .../late-bound-in-return-issue-77357.stderr | 11 +- .../min_const_generics/macro-fail.rs | 3 + .../min_const_generics/macro-fail.stderr | 28 ++- tests/ui/consts/escaping-bound-var.rs | 2 +- tests/ui/consts/escaping-bound-var.stderr | 20 +- tests/ui/consts/issue-103790.rs | 1 + tests/ui/consts/issue-103790.stderr | 11 +- tests/ui/derives/issue-97343.rs | 1 + tests/ui/derives/issue-97343.stderr | 14 +- tests/ui/did_you_mean/bad-assoc-ty.rs | 1 + tests/ui/did_you_mean/bad-assoc-ty.stderr | 20 +- .../replace-impl-infer-ty-from-trait.fixed | 1 + .../replace-impl-infer-ty-from-trait.rs | 1 + .../replace-impl-infer-ty-from-trait.stderr | 11 +- .../ui/dyn-keyword/dyn-2021-edition-error.rs | 1 + .../dyn-keyword/dyn-2021-edition-error.stderr | 13 +- tests/ui/error-codes/E0227.rs | 1 + tests/ui/error-codes/E0227.stderr | 22 +- tests/ui/error-codes/E0229.rs | 3 + tests/ui/error-codes/E0229.stderr | 32 ++- tests/ui/error-codes/E0719.rs | 2 + tests/ui/error-codes/E0719.stderr | 22 +- .../feature-gate-impl_trait_in_assoc_type.rs | 2 + ...ature-gate-impl_trait_in_assoc_type.stderr | 22 +- ...ture-gate-unboxed-closures-manual-impls.rs | 22 +- ...-gate-unboxed-closures-manual-impls.stderr | 90 ++++++-- tests/ui/fn/issue-39259.rs | 4 +- tests/ui/fn/issue-39259.stderr | 23 +- .../gat-in-trait-path-undeclared-lifetime.rs | 3 + ...t-in-trait-path-undeclared-lifetime.stderr | 37 ++- .../gat-trait-path-missing-lifetime.rs | 3 +- .../gat-trait-path-missing-lifetime.stderr | 25 +- .../gat-trait-path-parenthesised-args.rs | 9 + .../gat-trait-path-parenthesised-args.stderr | 143 +++++++++++- .../generic-associated-types/issue-71176.rs | 3 + .../issue-71176.stderr | 55 ++++- .../generic-associated-types/issue-79636-1.rs | 3 + .../issue-79636-1.stderr | 56 ++++- .../generic-associated-types/issue-80433.rs | 2 +- .../issue-80433.stderr | 13 +- .../missing_lifetime_args.rs | 3 + .../missing_lifetime_args.stderr | 58 ++++- .../trait-path-type-error-once-implemented.rs | 5 + ...it-path-type-error-once-implemented.stderr | 84 ++++++- .../type-param-defaults.rs | 2 + .../type-param-defaults.stderr | 40 +++- .../generic-const-items/parameter-defaults.rs | 1 + .../parameter-defaults.stderr | 14 +- tests/ui/generics/wrong-number-of-args.rs | 6 +- tests/ui/generics/wrong-number-of-args.stderr | 26 +-- ...plicit-hrtb-without-dyn.edition2021.stderr | 11 +- .../generic-with-implicit-hrtb-without-dyn.rs | 1 + tests/ui/impl-trait/impl-fn-hrtb-bounds.rs | 3 + .../ui/impl-trait/impl-fn-hrtb-bounds.stderr | 39 +++- .../impl-trait/impl-fn-parsing-ambiguities.rs | 1 + .../impl-fn-parsing-ambiguities.stderr | 13 +- tests/ui/impl-trait/impl_trait_projections.rs | 4 +- .../impl-trait/impl_trait_projections.stderr | 46 +++- .../impl-trait/implicit-capture-late.stderr | 8 +- tests/ui/impl-trait/issues/issue-67830.rs | 2 + tests/ui/impl-trait/issues/issue-67830.stderr | 21 +- tests/ui/impl-trait/issues/issue-88236-2.rs | 5 + .../ui/impl-trait/issues/issue-88236-2.stderr | 60 ++++- tests/ui/impl-trait/issues/issue-92305.rs | 1 + tests/ui/impl-trait/issues/issue-92305.stderr | 16 +- tests/ui/impl-trait/nested-rpit-hrtb.rs | 6 + tests/ui/impl-trait/nested-rpit-hrtb.stderr | 67 +++++- .../ui/intrinsics/safe-intrinsic-mismatch.rs | 2 + .../intrinsics/safe-intrinsic-mismatch.stderr | 20 +- tests/ui/issues/issue-31910.rs | 1 + tests/ui/issues/issue-31910.stderr | 15 +- tests/ui/issues/issue-3214.rs | 1 + tests/ui/issues/issue-3214.stderr | 10 +- tests/ui/issues/issue-34373.rs | 2 + tests/ui/issues/issue-34373.stderr | 38 +++- .../lang-item-generic-requirements.rs | 7 +- tests/ui/lifetimes/issue-95023.rs | 4 +- tests/ui/lifetimes/issue-95023.stderr | 44 ++-- .../ui/lifetimes/missing-lifetime-in-alias.rs | 3 + .../missing-lifetime-in-alias.stderr | 31 ++- ...lifetime-default-dyn-binding-nonstatic3.rs | 1 + ...time-default-dyn-binding-nonstatic3.stderr | 18 +- .../object-safety-supertrait-mentions-Self.rs | 1 + ...ect-safety-supertrait-mentions-Self.stderr | 27 ++- .../impl-item-type-no-body-semantic-fail.rs | 1 + ...mpl-item-type-no-body-semantic-fail.stderr | 14 +- .../issues/issue-73568-lifetime-after-mut.rs | 1 + .../issue-73568-lifetime-after-mut.stderr | 8 +- .../const-impl-requires-const-trait.stderr | 12 +- .../derive-const-non-const-type.stderr | 8 +- .../const_derives/derive-const-use.stderr | 19 +- .../ice-112822-expected-type-for-param.rs | 2 + .../ice-112822-expected-type-for-param.stderr | 47 +++- .../super-traits-fail-2.nn.stderr | 10 +- .../super-traits-fail-2.ny.stderr | 10 +- .../super-traits-fail-2.rs | 3 +- .../super-traits-fail-2.yn.stderr | 2 +- .../super-traits-fail-2.yy.stderr | 2 +- .../super-traits-fail-3.nn.stderr | 12 +- .../super-traits-fail-3.ny.stderr | 10 +- .../super-traits-fail-3.rs | 4 +- .../super-traits-fail-3.yn.stderr | 14 +- .../tilde-const-invalid-places.rs | 3 + .../tilde-const-invalid-places.stderr | 97 +++++--- .../generics-default-stability-where.rs | 1 + .../generics-default-stability-where.stderr | 14 +- .../ui/suggestions/bad-infer-in-trait-impl.rs | 1 + .../bad-infer-in-trait-impl.stderr | 14 +- tests/ui/suggestions/fn-trait-notation.fixed | 1 + tests/ui/suggestions/fn-trait-notation.rs | 1 + tests/ui/suggestions/fn-trait-notation.stderr | 18 +- .../impl-trait-missing-lifetime-gated.rs | 6 +- .../impl-trait-missing-lifetime-gated.stderr | 38 +++- .../suggestions/missing-lifetime-specifier.rs | 13 +- .../missing-lifetime-specifier.stderr | 183 ++------------- tests/ui/tag-type-args.rs | 1 + tests/ui/tag-type-args.stderr | 16 +- tests/ui/target-feature/invalid-attribute.rs | 4 +- .../target-feature/invalid-attribute.stderr | 22 +- ...lid-assoc-type-suggestion-in-trait-impl.rs | 3 + ...assoc-type-suggestion-in-trait-impl.stderr | 39 +++- .../ui/traits/bound/not-on-bare-trait-2021.rs | 2 + .../bound/not-on-bare-trait-2021.stderr | 43 +++- tests/ui/traits/issue-106072.rs | 1 + tests/ui/traits/issue-106072.stderr | 19 +- tests/ui/traits/issue-28576.rs | 2 + tests/ui/traits/issue-28576.stderr | 47 +++- tests/ui/traits/issue-38404.rs | 3 +- tests/ui/traits/issue-38404.stderr | 25 +- tests/ui/traits/issue-87558.rs | 2 + tests/ui/traits/issue-87558.stderr | 26 ++- .../late-bound-in-anon-ct.rs | 2 +- .../object-unsafe-missing-assoc-type.rs | 3 + .../object-unsafe-missing-assoc-type.stderr | 49 +++- tests/ui/transmutability/issue-101739-2.rs | 1 + .../ui/transmutability/issue-101739-2.stderr | 18 +- tests/ui/type-alias-impl-trait/issue-77179.rs | 4 +- .../type-alias-impl-trait/issue-77179.stderr | 26 ++- tests/ui/typeck/escaping_bound_vars.rs | 4 + tests/ui/typeck/escaping_bound_vars.stderr | 52 ++++- tests/ui/typeck/issue-110052.rs | 2 +- tests/ui/typeck/issue-79040.rs | 1 + tests/ui/typeck/issue-79040.stderr | 12 +- .../typeck-builtin-bound-type-parameters.rs | 5 +- ...ypeck-builtin-bound-type-parameters.stderr | 24 +- .../ui/typeck/typeck_type_placeholder_item.rs | 1 + .../typeck_type_placeholder_item.stderr | 37 +-- .../typeck_type_placeholder_item_help.rs | 4 +- .../typeck_type_placeholder_item_help.stderr | 27 ++- .../unboxed-closure-sugar-region.rs | 2 + .../unboxed-closure-sugar-region.stderr | 28 ++- ...gar-wrong-number-number-type-parameters.rs | 10 +- ...wrong-number-number-type-parameters.stderr | 68 +++--- 184 files changed, 2700 insertions(+), 631 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index b9e7500c8942..dfc54ac5b23b 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -166,13 +166,12 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { // this ensures that later parts of type checking can assume that items // have valid types and not error - // FIXME(matthewjasper) We shouldn't need to use `track_errors`. - tcx.sess.track_errors(|| { - tcx.sess.time("type_collecting", || { - tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module)) - }); - })?; + tcx.sess.time("type_collecting", || { + tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module)) + }); + // FIXME(matthewjasper) We shouldn't need to use `track_errors` anywhere in this function + // or the compiler in general. if tcx.features().rustc_attrs { tcx.sess.track_errors(|| { tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx)); diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index c52b8f37ef3b..080ad7bd549c 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -279,6 +279,12 @@ pub fn normalize_param_env_or_error<'tcx>( } fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + // FIXME(return_type_notation): track binders in this normalizer, as + // `ty::Const::normalize` can only work with properly preserved binders. + + if c.has_escaping_bound_vars() { + return ty::Const::new_misc_error(self.0, c.ty()); + } // While it is pretty sus to be evaluating things with an empty param env, it // should actually be okay since without `feature(generic_const_exprs)` the only // const arguments that have a non-empty param env are array repeat counts. These diff --git a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr index 2fceeb15ea98..3acec9c085a2 100644 --- a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr +++ b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr @@ -13,7 +13,7 @@ LL | impl Windows { note: struct defined here, with 1 generic parameter: `T` --> $DIR/issue-109071.rs:5:8 | -LL | struct Windows {} +LL | struct Windows { t: T } | ^^^^^^^ - help: add missing generic argument | @@ -30,7 +30,7 @@ LL | type Item = &[T]; = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable error[E0223]: ambiguous associated type - --> $DIR/issue-109071.rs:15:22 + --> $DIR/issue-109071.rs:16:22 | LL | fn T() -> Option {} | ^^^^^^^^^^ diff --git a/tests/ui/associated-inherent-types/issue-109071.rs b/tests/ui/associated-inherent-types/issue-109071.rs index 951c708e3f95..a897aaebc58f 100644 --- a/tests/ui/associated-inherent-types/issue-109071.rs +++ b/tests/ui/associated-inherent-types/issue-109071.rs @@ -2,18 +2,20 @@ #![cfg_attr(with_gate, feature(inherent_associated_types))] #![cfg_attr(with_gate, allow(incomplete_features))] -struct Windows {} +struct Windows { t: T } impl Windows { //~ ERROR: missing generics for struct `Windows` type Item = &[T]; //~ ERROR: `&` without an explicit lifetime name cannot be used here //[no_gate]~^ ERROR: inherent associated types are unstable fn next() -> Option {} + //[with_gate]~^ ERROR type annotations needed } impl Windows { fn T() -> Option {} //[no_gate]~^ ERROR: ambiguous associated type + //[with_gate]~^^ ERROR type annotations needed } fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr b/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr index a91bb7a5162e..d413c65dccb5 100644 --- a/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr +++ b/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr @@ -13,14 +13,26 @@ LL | impl Windows { note: struct defined here, with 1 generic parameter: `T` --> $DIR/issue-109071.rs:5:8 | -LL | struct Windows {} +LL | struct Windows { t: T } | ^^^^^^^ - help: add missing generic argument | LL | impl Windows { | +++ -error: aborting due to 2 previous errors +error[E0282]: type annotations needed + --> $DIR/issue-109071.rs:11:18 + | +LL | fn next() -> Option {} + | ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` -Some errors have detailed explanations: E0107, E0637. +error[E0282]: type annotations needed + --> $DIR/issue-109071.rs:16:15 + | +LL | fn T() -> Option {} + | ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0107, E0282, E0637. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/associated-inherent-types/issue-109299-1.rs b/tests/ui/associated-inherent-types/issue-109299-1.rs index 6f95273116b7..b86e2e31e064 100644 --- a/tests/ui/associated-inherent-types/issue-109299-1.rs +++ b/tests/ui/associated-inherent-types/issue-109299-1.rs @@ -8,5 +8,6 @@ impl Lexer { } type X = impl for Fn() -> Lexer::Cursor; //~ ERROR associated type `Cursor` not found for `Lexer` in the current scope +//~^ ERROR: unconstrained opaque type fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109299-1.stderr b/tests/ui/associated-inherent-types/issue-109299-1.stderr index c25ffb9d9c2a..5848fa4087da 100644 --- a/tests/ui/associated-inherent-types/issue-109299-1.stderr +++ b/tests/ui/associated-inherent-types/issue-109299-1.stderr @@ -10,6 +10,14 @@ LL | type X = impl for Fn() -> Lexer::Cursor; = note: the associated type was found for - `Lexer` -error: aborting due to 1 previous error +error: unconstrained opaque type + --> $DIR/issue-109299-1.rs:10:10 + | +LL | type X = impl for Fn() -> Lexer::Cursor; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `X` must be used in combination with a concrete type within the same module + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/issue-109768.rs b/tests/ui/associated-inherent-types/issue-109768.rs index a3ae2e2ab447..400f4f7de66f 100644 --- a/tests/ui/associated-inherent-types/issue-109768.rs +++ b/tests/ui/associated-inherent-types/issue-109768.rs @@ -8,5 +8,6 @@ impl Local { //~ ERROR missing generics for struct `Local` type AssocType3 = T; //~ ERROR inherent associated types are unstable const WRAPPED_ASSOC_3: Wrapper = Wrapper(); + //~^ ERROR: this struct takes 1 argument but 0 arguments were supplied } //~^ ERROR `main` function not found diff --git a/tests/ui/associated-inherent-types/issue-109768.stderr b/tests/ui/associated-inherent-types/issue-109768.stderr index 97706d4062a9..c489fd1ab9b0 100644 --- a/tests/ui/associated-inherent-types/issue-109768.stderr +++ b/tests/ui/associated-inherent-types/issue-109768.stderr @@ -1,5 +1,5 @@ error[E0601]: `main` function not found in crate `issue_109768` - --> $DIR/issue-109768.rs:11:2 + --> $DIR/issue-109768.rs:12:2 | LL | } | ^ consider adding a `main` function to `$DIR/issue-109768.rs` @@ -29,7 +29,23 @@ LL | type AssocType3 = T; = note: see issue #8995 for more information = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable -error: aborting due to 3 previous errors +error[E0061]: this struct takes 1 argument but 0 arguments were supplied + --> $DIR/issue-109768.rs:10:56 + | +LL | const WRAPPED_ASSOC_3: Wrapper = Wrapper(); + | ^^^^^^^-- an argument is missing + | +note: tuple struct defined here + --> $DIR/issue-109768.rs:3:8 + | +LL | struct Wrapper(T); + | ^^^^^^^ +help: provide the argument + | +LL | const WRAPPED_ASSOC_3: Wrapper = Wrapper(/* value */); + | ~~~~~~~~~~~~~ -Some errors have detailed explanations: E0107, E0601, E0658. -For more information about an error, try `rustc --explain E0107`. +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0061, E0107, E0601, E0658. +For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/associated-type-bounds/duplicate.rs b/tests/ui/associated-type-bounds/duplicate.rs index 5019804d4949..160b524c881c 100644 --- a/tests/ui/associated-type-bounds/duplicate.rs +++ b/tests/ui/associated-type-bounds/duplicate.rs @@ -134,14 +134,17 @@ where fn FRPIT1() -> impl Iterator { //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] iter::empty() + //~^ ERROR type annotations needed } fn FRPIT2() -> impl Iterator { //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] iter::empty() + //~^ ERROR type annotations needed } fn FRPIT3() -> impl Iterator { //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] iter::empty() + //~^ ERROR type annotations needed } fn FAPIT1(_: impl Iterator) {} //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] @@ -194,12 +197,15 @@ trait TRI3> {} trait TRS1: Iterator {} //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] trait TRS2: Iterator {} //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] trait TRS3: Iterator {} //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] trait TRW1 where T: Iterator, @@ -223,6 +229,7 @@ where Self: Iterator, //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] { } trait TRSW2 @@ -230,6 +237,7 @@ where Self: Iterator, //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] { } trait TRSW3 @@ -237,15 +245,19 @@ where Self: Iterator, //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] { } trait TRA1 { type A: Iterator; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR `<::A as Iterator>::Item` cannot be sent between threads safely + //~| ERROR the trait bound `<::A as Iterator>::Item: Copy` is not satisfied } trait TRA2 { type A: Iterator; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the trait bound `<::A as Iterator>::Item: Copy` is not satisfied } trait TRA3 { type A: Iterator; diff --git a/tests/ui/associated-type-bounds/duplicate.stderr b/tests/ui/associated-type-bounds/duplicate.stderr index 38b812dfdd44..accb366dd155 100644 --- a/tests/ui/associated-type-bounds/duplicate.stderr +++ b/tests/ui/associated-type-bounds/duplicate.stderr @@ -7,7 +7,7 @@ LL | struct SI1> { | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:255:40 + --> $DIR/duplicate.rs:267:40 | LL | type TADyn1 = dyn Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -15,7 +15,7 @@ LL | type TADyn1 = dyn Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:257:44 + --> $DIR/duplicate.rs:269:44 | LL | type TADyn2 = Box>; | ---------- ^^^^^^^^^^ re-bound here @@ -23,7 +23,7 @@ LL | type TADyn2 = Box>; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:259:43 + --> $DIR/duplicate.rs:271:43 | LL | type TADyn3 = dyn Iterator; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -223,7 +223,7 @@ LL | fn FRPIT1() -> impl Iterator { | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:138:42 + --> $DIR/duplicate.rs:139:42 | LL | fn FRPIT2() -> impl Iterator { | ---------- ^^^^^^^^^^ re-bound here @@ -231,7 +231,7 @@ LL | fn FRPIT2() -> impl Iterator { | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:142:45 + --> $DIR/duplicate.rs:144:45 | LL | fn FRPIT3() -> impl Iterator { | ------------- ^^^^^^^^^^^^^ re-bound here @@ -239,7 +239,7 @@ LL | fn FRPIT3() -> impl Iterator { | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:146:40 + --> $DIR/duplicate.rs:149:40 | LL | fn FAPIT1(_: impl Iterator) {} | ---------- ^^^^^^^^^^ re-bound here @@ -247,7 +247,7 @@ LL | fn FAPIT1(_: impl Iterator) {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:148:40 + --> $DIR/duplicate.rs:151:40 | LL | fn FAPIT2(_: impl Iterator) {} | ---------- ^^^^^^^^^^ re-bound here @@ -255,7 +255,7 @@ LL | fn FAPIT2(_: impl Iterator) {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:150:43 + --> $DIR/duplicate.rs:153:43 | LL | fn FAPIT3(_: impl Iterator) {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -263,7 +263,7 @@ LL | fn FAPIT3(_: impl Iterator) {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:153:35 + --> $DIR/duplicate.rs:156:35 | LL | type TAI1> = T; | ---------- ^^^^^^^^^^ re-bound here @@ -271,7 +271,7 @@ LL | type TAI1> = T; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:155:35 + --> $DIR/duplicate.rs:158:35 | LL | type TAI2> = T; | ---------- ^^^^^^^^^^ re-bound here @@ -279,7 +279,7 @@ LL | type TAI2> = T; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:157:38 + --> $DIR/duplicate.rs:160:38 | LL | type TAI3> = T; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -287,7 +287,7 @@ LL | type TAI3> = T; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:161:29 + --> $DIR/duplicate.rs:164:29 | LL | T: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -295,7 +295,7 @@ LL | T: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:166:29 + --> $DIR/duplicate.rs:169:29 | LL | T: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -303,7 +303,7 @@ LL | T: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:171:32 + --> $DIR/duplicate.rs:174:32 | LL | T: Iterator, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -311,7 +311,7 @@ LL | T: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:175:36 + --> $DIR/duplicate.rs:178:36 | LL | type ETAI1> = impl Copy; | ---------- ^^^^^^^^^^ re-bound here @@ -319,7 +319,7 @@ LL | type ETAI1> = impl Copy; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:177:36 + --> $DIR/duplicate.rs:180:36 | LL | type ETAI2> = impl Copy; | ---------- ^^^^^^^^^^ re-bound here @@ -327,7 +327,7 @@ LL | type ETAI2> = impl Copy; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:179:39 + --> $DIR/duplicate.rs:182:39 | LL | type ETAI3> = impl Copy; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -335,7 +335,7 @@ LL | type ETAI3> = impl Copy; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:181:40 + --> $DIR/duplicate.rs:184:40 | LL | type ETAI4 = impl Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -343,7 +343,7 @@ LL | type ETAI4 = impl Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:183:40 + --> $DIR/duplicate.rs:186:40 | LL | type ETAI5 = impl Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -351,7 +351,7 @@ LL | type ETAI5 = impl Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:185:43 + --> $DIR/duplicate.rs:188:43 | LL | type ETAI6 = impl Iterator; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -359,7 +359,7 @@ LL | type ETAI6 = impl Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:188:36 + --> $DIR/duplicate.rs:191:36 | LL | trait TRI1> {} | ---------- ^^^^^^^^^^ re-bound here @@ -367,7 +367,7 @@ LL | trait TRI1> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:190:36 + --> $DIR/duplicate.rs:193:36 | LL | trait TRI2> {} | ---------- ^^^^^^^^^^ re-bound here @@ -375,7 +375,7 @@ LL | trait TRI2> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:192:39 + --> $DIR/duplicate.rs:195:39 | LL | trait TRI3> {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -383,7 +383,7 @@ LL | trait TRI3> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:194:34 + --> $DIR/duplicate.rs:197:34 | LL | trait TRS1: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -391,7 +391,7 @@ LL | trait TRS1: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:194:34 + --> $DIR/duplicate.rs:197:34 | LL | trait TRS1: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -401,7 +401,7 @@ LL | trait TRS1: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:197:34 + --> $DIR/duplicate.rs:201:34 | LL | trait TRS2: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -409,7 +409,7 @@ LL | trait TRS2: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:197:34 + --> $DIR/duplicate.rs:201:34 | LL | trait TRS2: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -419,7 +419,7 @@ LL | trait TRS2: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:200:37 + --> $DIR/duplicate.rs:205:37 | LL | trait TRS3: Iterator {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -427,7 +427,7 @@ LL | trait TRS3: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:200:37 + --> $DIR/duplicate.rs:205:37 | LL | trait TRS3: Iterator {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -437,7 +437,7 @@ LL | trait TRS3: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:205:29 + --> $DIR/duplicate.rs:211:29 | LL | T: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -445,7 +445,7 @@ LL | T: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:211:29 + --> $DIR/duplicate.rs:217:29 | LL | T: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -453,7 +453,7 @@ LL | T: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:217:32 + --> $DIR/duplicate.rs:223:32 | LL | T: Iterator, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -461,7 +461,7 @@ LL | T: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:223:32 + --> $DIR/duplicate.rs:229:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -469,7 +469,7 @@ LL | Self: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:223:32 + --> $DIR/duplicate.rs:229:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -479,7 +479,7 @@ LL | Self: Iterator, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:230:32 + --> $DIR/duplicate.rs:237:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -487,7 +487,7 @@ LL | Self: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:230:32 + --> $DIR/duplicate.rs:237:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -497,7 +497,7 @@ LL | Self: Iterator, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:237:35 + --> $DIR/duplicate.rs:245:35 | LL | Self: Iterator, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -505,7 +505,7 @@ LL | Self: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:237:35 + --> $DIR/duplicate.rs:245:35 | LL | Self: Iterator, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -515,7 +515,7 @@ LL | Self: Iterator, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:243:34 + --> $DIR/duplicate.rs:252:34 | LL | type A: Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -523,7 +523,7 @@ LL | type A: Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:247:34 + --> $DIR/duplicate.rs:258:34 | LL | type A: Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -531,13 +531,141 @@ LL | type A: Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:251:37 + --> $DIR/duplicate.rs:263:37 | LL | type A: Iterator; | ------------- ^^^^^^^^^^^^^ re-bound here | | | `Item` bound here first -error: aborting due to 66 previous errors +error[E0282]: type annotations needed + --> $DIR/duplicate.rs:136:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ -For more information about this error, try `rustc --explain E0719`. +error[E0282]: type annotations needed + --> $DIR/duplicate.rs:141:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ + +error[E0282]: type annotations needed + --> $DIR/duplicate.rs:146:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:197:34 + | +LL | trait TRS1: Iterator {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:201:34 + | +LL | trait TRS2: Iterator {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:205:37 + | +LL | trait TRS3: Iterator {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:229:32 + | +LL | Self: Iterator, + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:237:32 + | +LL | Self: Iterator, + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:245:35 + | +LL | Self: Iterator, + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `<::A as Iterator>::Item: Copy` is not satisfied + --> $DIR/duplicate.rs:252:28 + | +LL | type A: Iterator; + | ^^^^ the trait `Copy` is not implemented for `<::A as Iterator>::Item` + | +help: consider further restricting the associated type + | +LL | trait TRA1 where <::A as Iterator>::Item: Copy { + | +++++++++++++++++++++++++++++++++++++++++++++++++ + +error[E0277]: `<::A as Iterator>::Item` cannot be sent between threads safely + --> $DIR/duplicate.rs:252:40 + | +LL | type A: Iterator; + | ^^^^ `<::A as Iterator>::Item` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `<::A as Iterator>::Item` +help: consider further restricting the associated type + | +LL | trait TRA1 where <::A as Iterator>::Item: Send { + | +++++++++++++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `<::A as Iterator>::Item: Copy` is not satisfied + --> $DIR/duplicate.rs:258:28 + | +LL | type A: Iterator; + | ^^^^ the trait `Copy` is not implemented for `<::A as Iterator>::Item` + | +help: consider further restricting the associated type + | +LL | trait TRA2 where <::A as Iterator>::Item: Copy { + | +++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 78 previous errors + +Some errors have detailed explanations: E0277, E0282, E0719. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-types/associated-type-projection-ambig-between-bound-and-where-clause.rs b/tests/ui/associated-types/associated-type-projection-ambig-between-bound-and-where-clause.rs index c85d41c7f519..eaf3090bbd38 100644 --- a/tests/ui/associated-types/associated-type-projection-ambig-between-bound-and-where-clause.rs +++ b/tests/ui/associated-types/associated-type-projection-ambig-between-bound-and-where-clause.rs @@ -25,7 +25,7 @@ fn c(_: C::Color) where C : Vehicle, C : Box { //~^ ERROR ambiguous associated type `Color` in bounds of `C` } -struct D; +struct D(X); impl D where X : Vehicle { fn d(&self, _: X::Color) where X : Box { } //~^ ERROR ambiguous associated type `Color` in bounds of `X` diff --git a/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.rs b/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.rs index df19332b6458..b757521e0a45 100644 --- a/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.rs +++ b/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.rs @@ -20,7 +20,7 @@ fn dent(c: C, color: C::Color) { //~^ ERROR ambiguous associated type `Color` in bounds of `C` } -fn dent_object(c: dyn BoxCar) { +fn dent_object(c: &dyn BoxCar) { //~^ ERROR ambiguous associated type //~| ERROR the value of the associated types } @@ -29,7 +29,7 @@ fn paint(c: C, d: C::Color) { //~^ ERROR ambiguous associated type `Color` in bounds of `C` } -fn dent_object_2(c: dyn BoxCar) where ::Color = COLOR { +fn dent_object_2(c: &dyn BoxCar) where ::Color = COLOR { //~^ ERROR the value of the associated types //~| ERROR equality constraints are not yet supported in `where` clauses } diff --git a/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr b/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr index 66037054e064..a4874903285a 100644 --- a/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr +++ b/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr @@ -1,8 +1,8 @@ error: equality constraints are not yet supported in `where` clauses - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:46 + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:47 | -LL | fn dent_object_2(c: dyn BoxCar) where ::Color = COLOR { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not supported +LL | fn dent_object_2(c: &dyn BoxCar) where ::Color = COLOR { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not supported | = note: see issue #20041 for more information @@ -28,7 +28,7 @@ LL | fn dent(c: C, color: ::Color) { | ~~~~~~~~~~~~ error[E0222]: ambiguous associated type `Color` in bounds of `BoxCar` - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:37 + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:38 | LL | type Color; | ---------- ambiguous `Color` from `Vehicle` @@ -36,8 +36,8 @@ LL | type Color; LL | type Color; | ---------- ambiguous `Color` from `Box` ... -LL | fn dent_object(c: dyn BoxCar) { - | ^^^^^^^^^^^ ambiguous associated type `Color` +LL | fn dent_object(c: &dyn BoxCar) { + | ^^^^^^^^^^^ ambiguous associated type `Color` | = help: consider introducing a new type parameter `T` and adding `where` constraints: where @@ -46,7 +46,7 @@ LL | fn dent_object(c: dyn BoxCar) { T: Box::Color = COLOR error[E0191]: the value of the associated types `Color` in `Box`, `Color` in `Vehicle` must be specified - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30 + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:31 | LL | type Color; | ---------- `Vehicle::Color` defined here @@ -54,8 +54,8 @@ LL | type Color; LL | type Color; | ---------- `Box::Color` defined here ... -LL | fn dent_object(c: dyn BoxCar) { - | ^^^^^^^^^^^^^^^^^^^ associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified +LL | fn dent_object(c: &dyn BoxCar) { + | ^^^^^^^^^^^^^^^^^^^ associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified | = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types @@ -81,7 +81,7 @@ LL | fn paint(c: C, d: ::Color) { | ~~~~~~~~~~~~ error[E0191]: the value of the associated types `Color` in `Box`, `Color` in `Vehicle` must be specified - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:32 + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:33 | LL | type Color; | ---------- `Vehicle::Color` defined here @@ -89,8 +89,8 @@ LL | type Color; LL | type Color; | ---------- `Box::Color` defined here ... -LL | fn dent_object_2(c: dyn BoxCar) where ::Color = COLOR { - | ^^^^^^ associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified +LL | fn dent_object_2(c: &dyn BoxCar) where ::Color = COLOR { + | ^^^^^^ associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified | = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types diff --git a/tests/ui/associated-types/issue-23595-1.rs b/tests/ui/associated-types/issue-23595-1.rs index 483c205f42d6..9222f5b66509 100644 --- a/tests/ui/associated-types/issue-23595-1.rs +++ b/tests/ui/associated-types/issue-23595-1.rs @@ -7,6 +7,7 @@ trait Hierarchy { type ChildKey; type Children = dyn Index; //~^ ERROR: the value of the associated types + //~| ERROR: the size for values of type fn data(&self) -> Option<(Self::Value, Self::Children)>; } diff --git a/tests/ui/associated-types/issue-23595-1.stderr b/tests/ui/associated-types/issue-23595-1.stderr index f9d58c23cbba..46906ab3fb7a 100644 --- a/tests/ui/associated-types/issue-23595-1.stderr +++ b/tests/ui/associated-types/issue-23595-1.stderr @@ -8,6 +8,20 @@ LL | type ChildKey; LL | type Children = dyn Index; | ------------- `Children` defined here ^^^^^^^^^ help: specify the associated types: `Hierarchy` -error: aborting due to 1 previous error +error[E0277]: the size for values of type `(dyn Index<::ChildKey, Output = (dyn Hierarchy + 'static)> + 'static)` cannot be known at compilation time + --> $DIR/issue-23595-1.rs:8:21 + | +LL | type Children = dyn Index; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Index<::ChildKey, Output = (dyn Hierarchy + 'static)> + 'static)` +note: required by a bound in `Hierarchy::Children` + --> $DIR/issue-23595-1.rs:8:5 + | +LL | type Children = dyn Index; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Hierarchy::Children` -For more information about this error, try `rustc --explain E0191`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0191, E0277. +For more information about an error, try `rustc --explain E0191`. diff --git a/tests/ui/async-await/issues/issue-65159.rs b/tests/ui/async-await/issues/issue-65159.rs index 6e547508bd46..aed111e21442 100644 --- a/tests/ui/async-await/issues/issue-65159.rs +++ b/tests/ui/async-await/issues/issue-65159.rs @@ -5,7 +5,7 @@ async fn copy() -> Result<()> //~^ ERROR enum takes 2 generic arguments { - Ok(()) + Ok(()) //~ ERROR: type annotations needed } fn main() { } diff --git a/tests/ui/async-await/issues/issue-65159.stderr b/tests/ui/async-await/issues/issue-65159.stderr index 19512116a66c..77a0ea5027c1 100644 --- a/tests/ui/async-await/issues/issue-65159.stderr +++ b/tests/ui/async-await/issues/issue-65159.stderr @@ -11,6 +11,18 @@ help: add missing generic argument LL | async fn copy() -> Result<(), E> | +++ -error: aborting due to 1 previous error +error[E0282]: type annotations needed + --> $DIR/issue-65159.rs:8:5 + | +LL | Ok(()) + | ^^ cannot infer type of the type parameter `E` declared on the enum `Result` + | +help: consider specifying the generic arguments + | +LL | Ok::<(), E>(()) + | +++++++++ -For more information about this error, try `rustc --explain E0107`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0107, E0282. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.rs b/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.rs index 1b16a492a7a1..68a750778ada 100644 --- a/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.rs +++ b/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.rs @@ -9,5 +9,6 @@ trait Super1<'a> { impl Super1<'_, bar(): Send> for () {} //~^ ERROR associated type bindings are not allowed here +//~| ERROR not all trait items implemented fn main() {} diff --git a/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.stderr b/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.stderr index 4321d876e163..d925c7316b6e 100644 --- a/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.stderr +++ b/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.stderr @@ -13,6 +13,16 @@ error[E0229]: associated type bindings are not allowed here LL | impl Super1<'_, bar(): Send> for () {} | ^^^^^^^^^^^ associated type not allowed here -error: aborting due to 1 previous error; 1 warning emitted +error[E0046]: not all trait items implemented, missing: `bar` + --> $DIR/rtn-in-impl-signature.rs:10:1 + | +LL | fn bar<'b>() -> bool; + | --------------------- `bar` from trait +... +LL | impl Super1<'_, bar(): Send> for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `bar` in implementation -For more information about this error, try `rustc --explain E0229`. +error: aborting due to 2 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0046, E0229. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr index 739c04a76730..e23fc459358b 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr @@ -26,7 +26,7 @@ LL | let _ = #[track_caller] || { = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:28:17 + --> $DIR/async-closure-gate.rs:29:17 | LL | let _ = #[track_caller] || { | ^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | let _ = #[track_caller] || { = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:36:9 + --> $DIR/async-closure-gate.rs:37:9 | LL | #[track_caller] || { | ^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | #[track_caller] || { = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:45:13 + --> $DIR/async-closure-gate.rs:47:13 | LL | #[track_caller] || { | ^^^^^^^^^^^^^^^ @@ -52,6 +52,40 @@ LL | #[track_caller] || { = note: see issue #87417 for more information = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable -error: aborting due to 6 previous errors +error[E0308]: mismatched types + --> $DIR/async-closure-gate.rs:27:5 + | +LL | fn foo3() { + | - help: a return type might be missing here: `-> _` +LL | / async { +LL | | +LL | | let _ = #[track_caller] || { +LL | | +LL | | }; +LL | | } + | |_____^ expected `()`, found `async` block + | + = note: expected unit type `()` + found `async` block `{async block@$DIR/async-closure-gate.rs:27:5: 32:6}` -For more information about this error, try `rustc --explain E0658`. +error[E0308]: mismatched types + --> $DIR/async-closure-gate.rs:44:5 + | +LL | fn foo5() { + | - help: a return type might be missing here: `-> _` +LL | / async { +LL | | +LL | | let _ = || { +LL | | #[track_caller] || { +... | +LL | | }; +LL | | } + | |_____^ expected `()`, found `async` block + | + = note: expected unit type `()` + found `async` block `{async block@$DIR/async-closure-gate.rs:44:5: 51:6}` + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr index 739c04a76730..e23fc459358b 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr @@ -26,7 +26,7 @@ LL | let _ = #[track_caller] || { = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:28:17 + --> $DIR/async-closure-gate.rs:29:17 | LL | let _ = #[track_caller] || { | ^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | let _ = #[track_caller] || { = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:36:9 + --> $DIR/async-closure-gate.rs:37:9 | LL | #[track_caller] || { | ^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | #[track_caller] || { = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:45:13 + --> $DIR/async-closure-gate.rs:47:13 | LL | #[track_caller] || { | ^^^^^^^^^^^^^^^ @@ -52,6 +52,40 @@ LL | #[track_caller] || { = note: see issue #87417 for more information = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable -error: aborting due to 6 previous errors +error[E0308]: mismatched types + --> $DIR/async-closure-gate.rs:27:5 + | +LL | fn foo3() { + | - help: a return type might be missing here: `-> _` +LL | / async { +LL | | +LL | | let _ = #[track_caller] || { +LL | | +LL | | }; +LL | | } + | |_____^ expected `()`, found `async` block + | + = note: expected unit type `()` + found `async` block `{async block@$DIR/async-closure-gate.rs:27:5: 32:6}` -For more information about this error, try `rustc --explain E0658`. +error[E0308]: mismatched types + --> $DIR/async-closure-gate.rs:44:5 + | +LL | fn foo5() { + | - help: a return type might be missing here: `-> _` +LL | / async { +LL | | +LL | | let _ = || { +LL | | #[track_caller] || { +... | +LL | | }; +LL | | } + | |_____^ expected `()`, found `async` block + | + = note: expected unit type `()` + found `async` block `{async block@$DIR/async-closure-gate.rs:44:5: 51:6}` + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.rs b/tests/ui/async-await/track-caller/async-closure-gate.rs index 8d8d081aa90b..911934a22327 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.rs +++ b/tests/ui/async-await/track-caller/async-closure-gate.rs @@ -25,6 +25,7 @@ async fn foo2() { fn foo3() { async { + //~^ ERROR mismatched types let _ = #[track_caller] || { //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] }; @@ -41,6 +42,7 @@ async fn foo4() { fn foo5() { async { + //~^ ERROR mismatched types let _ = || { #[track_caller] || { //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] diff --git a/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs b/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs index b52939ffc119..c10246eec649 100644 --- a/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs +++ b/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs @@ -17,6 +17,7 @@ async fn buy_lock(coroutine: &Mutex) -> LockedMarket<'_> { //~^ ERROR struct takes 0 lifetime arguments but 1 lifetime argument was supplied //~^^ ERROR struct takes 1 generic argument but 0 generic arguments were supplied LockedMarket(coroutine.lock().unwrap().buy()) + //~^ ERROR: cannot return value referencing temporary value } struct LockedMarket(T); diff --git a/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr b/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr index 516c1d065e67..2b10cf67d15b 100644 --- a/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr +++ b/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr @@ -7,7 +7,7 @@ LL | async fn buy_lock(coroutine: &Mutex) -> LockedMarket<'_> | expected 0 lifetime arguments | note: struct defined here, with 0 lifetime parameters - --> $DIR/issue-82126-mismatched-subst-and-hir.rs:22:8 + --> $DIR/issue-82126-mismatched-subst-and-hir.rs:23:8 | LL | struct LockedMarket(T); | ^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | async fn buy_lock(coroutine: &Mutex) -> LockedMarket<'_> | ^^^^^^^^^^^^ expected 1 generic argument | note: struct defined here, with 1 generic parameter: `T` - --> $DIR/issue-82126-mismatched-subst-and-hir.rs:22:8 + --> $DIR/issue-82126-mismatched-subst-and-hir.rs:23:8 | LL | struct LockedMarket(T); | ^^^^^^^^^^^^ - @@ -28,6 +28,16 @@ help: add missing generic argument LL | async fn buy_lock(coroutine: &Mutex) -> LockedMarket<'_, T> { | +++ -error: aborting due to 2 previous errors +error[E0515]: cannot return value referencing temporary value + --> $DIR/issue-82126-mismatched-subst-and-hir.rs:19:5 + | +LL | LockedMarket(coroutine.lock().unwrap().buy()) + | ^^^^^^^^^^^^^-------------------------^^^^^^^ + | | | + | | temporary value created here + | returns a value referencing data owned by the current function -For more information about this error, try `rustc --explain E0107`. +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0515. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/const-generics/assoc_const_eq_diagnostic.rs b/tests/ui/const-generics/assoc_const_eq_diagnostic.rs index d51696f9ebde..573d8055a79c 100644 --- a/tests/ui/const-generics/assoc_const_eq_diagnostic.rs +++ b/tests/ui/const-generics/assoc_const_eq_diagnostic.rs @@ -9,9 +9,10 @@ pub trait Parse { } pub trait CoolStuff: Parse {} -//~^ ERROR expected type, found variant +//~^ ERROR expected constant, found type //~| ERROR expected constant, found type //~| ERROR expected constant, found type +//~| ERROR expected type fn no_help() -> Mode::Cool {} //~^ ERROR expected type, found variant diff --git a/tests/ui/const-generics/assoc_const_eq_diagnostic.stderr b/tests/ui/const-generics/assoc_const_eq_diagnostic.stderr index 3d724bb16424..13f081940f39 100644 --- a/tests/ui/const-generics/assoc_const_eq_diagnostic.stderr +++ b/tests/ui/const-generics/assoc_const_eq_diagnostic.stderr @@ -8,7 +8,7 @@ LL | pub trait CoolStuff: Parse {} | help: try using the variant's enum: `Mode` error[E0573]: expected type, found variant `Mode::Cool` - --> $DIR/assoc_const_eq_diagnostic.rs:16:17 + --> $DIR/assoc_const_eq_diagnostic.rs:17:17 | LL | fn no_help() -> Mode::Cool {} | ^^^^^^^^^^ @@ -53,6 +53,25 @@ help: consider adding braces here LL | pub trait CoolStuff: Parse {} | + + -error: aborting due to 4 previous errors +error: expected constant, found type + --> $DIR/assoc_const_eq_diagnostic.rs:11:35 + | +LL | pub trait CoolStuff: Parse {} + | ---- ^^^^^^^^^^ unexpected type + | | + | expected a constant because of this associated constant + | +note: the associated constant is defined here + --> $DIR/assoc_const_eq_diagnostic.rs:8:5 + | +LL | const MODE: Mode; + | ^^^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding braces here + | +LL | pub trait CoolStuff: Parse {} + | + + + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0573`. diff --git a/tests/ui/const-generics/generic_const_exprs/issue-102768.rs b/tests/ui/const-generics/generic_const_exprs/issue-102768.rs index 18a9b53cf768..f2ad7d7ce8b9 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-102768.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-102768.rs @@ -9,6 +9,11 @@ const _: () = { fn f2<'a>(arg: Box = &'a ()>>) {} //~^ ERROR associated type takes 1 lifetime argument but 0 lifetime arguments //~| ERROR associated type takes 0 generic arguments but 1 generic argument + //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR associated type takes 0 generic arguments but 1 generic argument + //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR associated type takes 0 generic arguments but 1 generic argument + //~| ERROR `X` cannot be made into an object }; fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr b/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr index 175d54e41848..a470c36134cf 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr @@ -28,6 +28,86 @@ note: associated type defined here, with 0 generic parameters LL | type Y<'a>; | ^ -error: aborting due to 2 previous errors +error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/issue-102768.rs:9:30 + | +LL | fn f2<'a>(arg: Box = &'a ()>>) {} + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/issue-102768.rs:5:10 + | +LL | type Y<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | fn f2<'a>(arg: Box = &'a ()>>) {} + | +++ -For more information about this error, try `rustc --explain E0107`. +error[E0107]: associated type takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/issue-102768.rs:9:30 + | +LL | fn f2<'a>(arg: Box = &'a ()>>) {} + | ^--- help: remove these generics + | | + | expected 0 generic arguments + | +note: associated type defined here, with 0 generic parameters + --> $DIR/issue-102768.rs:5:10 + | +LL | type Y<'a>; + | ^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/issue-102768.rs:9:30 + | +LL | fn f2<'a>(arg: Box = &'a ()>>) {} + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/issue-102768.rs:5:10 + | +LL | type Y<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | fn f2<'a>(arg: Box = &'a ()>>) {} + | +++ + +error[E0107]: associated type takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/issue-102768.rs:9:30 + | +LL | fn f2<'a>(arg: Box = &'a ()>>) {} + | ^--- help: remove these generics + | | + | expected 0 generic arguments + | +note: associated type defined here, with 0 generic parameters + --> $DIR/issue-102768.rs:5:10 + | +LL | type Y<'a>; + | ^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0038]: the trait `X` cannot be made into an object + --> $DIR/issue-102768.rs:9:24 + | +LL | fn f2<'a>(arg: Box = &'a ()>>) {} + | ^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/issue-102768.rs:5:10 + | +LL | trait X { + | - this trait cannot be made into an object... +LL | type Y<'a>; + | ^ ...because it contains the generic associated type `Y` + = help: consider moving `Y` to another trait + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0038, E0107. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/generic_const_exprs/issue-105257.rs b/tests/ui/const-generics/generic_const_exprs/issue-105257.rs index d8b23bc01a96..a107556fd79d 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-105257.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-105257.rs @@ -3,6 +3,7 @@ trait Trait { fn fnc(&self) {} //~ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + //~^ ERROR: mismatched types fn foo() }>(&self) {} //~ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions } diff --git a/tests/ui/const-generics/generic_const_exprs/issue-105257.stderr b/tests/ui/const-generics/generic_const_exprs/issue-105257.stderr index ed7a8cb19a4a..d7ded0f1f748 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-105257.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-105257.stderr @@ -5,10 +5,17 @@ LL | fn fnc(&self) {} | ^^^^^^^^^^^^^^^^^^^ error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/issue-105257.rs:6:12 + --> $DIR/issue-105257.rs:7:12 | LL | fn foo() }>(&self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/issue-105257.rs:5:29 + | +LL | fn fnc(&self) {} + | ^^ expected `usize`, found `&str` +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr b/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr index 7bef98b1d5d2..1fe0109771c5 100644 --- a/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr +++ b/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr @@ -4,5 +4,14 @@ error: cannot capture late-bound lifetime in constant LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { | -- lifetime defined here ^^ -error: aborting due to 1 previous error +error: overly complex generic constant + --> $DIR/late-bound-in-return-issue-77357.rs:9:46 + | +LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constants + | + = help: consider moving this anonymous constant into a `const` function + = note: this operation may be supported in the future + +error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/min_const_generics/macro-fail.rs b/tests/ui/const-generics/min_const_generics/macro-fail.rs index 7fb69032e6fc..f3df96d468c3 100644 --- a/tests/ui/const-generics/min_const_generics/macro-fail.rs +++ b/tests/ui/const-generics/min_const_generics/macro-fail.rs @@ -14,6 +14,7 @@ impl Marker for Example {} fn make_marker() -> impl Marker { //~^ ERROR: type provided when a constant was expected Example:: + //~^ ERROR: type provided when a constant was expected } fn from_marker(_: impl Marker<{ @@ -33,7 +34,9 @@ fn main() { }>; let _fail = Example::; + //~^ ERROR: type provided when a constant was expected let _fail = Example::; //~^ ERROR unexpected end of macro invocation + //~| ERROR: type provided when a constant was expected } diff --git a/tests/ui/const-generics/min_const_generics/macro-fail.stderr b/tests/ui/const-generics/min_const_generics/macro-fail.stderr index cc629fd920fa..06a111008a30 100644 --- a/tests/ui/const-generics/min_const_generics/macro-fail.stderr +++ b/tests/ui/const-generics/min_const_generics/macro-fail.stderr @@ -1,5 +1,5 @@ error: expected type, found `{` - --> $DIR/macro-fail.rs:28:27 + --> $DIR/macro-fail.rs:29:27 | LL | fn make_marker() -> impl Marker { | ---------------------- @@ -13,7 +13,7 @@ LL | ($rusty: ident) => {{ let $rusty = 3; *&$rusty }} = note: this error originates in the macro `gimme_a_const` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected type, found `{` - --> $DIR/macro-fail.rs:28:27 + --> $DIR/macro-fail.rs:29:27 | LL | Example:: | ---------------------- @@ -41,7 +41,7 @@ LL | let _fail = Example::; = note: this error originates in the macro `external_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: unexpected end of macro invocation - --> $DIR/macro-fail.rs:37:25 + --> $DIR/macro-fail.rs:39:25 | LL | macro_rules! gimme_a_const { | -------------------------- when calling this macro @@ -50,7 +50,7 @@ LL | let _fail = Example::; | ^^^^^^^^^^^^^^^^ missing tokens in macro arguments | note: while trying to match meta-variable `$rusty:ident` - --> $DIR/macro-fail.rs:28:8 + --> $DIR/macro-fail.rs:29:8 | LL | ($rusty: ident) => {{ let $rusty = 3; *&$rusty }} | ^^^^^^^^^^^^^ @@ -61,6 +61,24 @@ error[E0747]: type provided when a constant was expected LL | fn make_marker() -> impl Marker { | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error[E0747]: type provided when a constant was expected + --> $DIR/macro-fail.rs:16:13 + | +LL | Example:: + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0747]: type provided when a constant was expected + --> $DIR/macro-fail.rs:36:25 + | +LL | let _fail = Example::; + | ^^^^^^^^^^^^^^^^^ + +error[E0747]: type provided when a constant was expected + --> $DIR/macro-fail.rs:39:25 + | +LL | let _fail = Example::; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0747`. diff --git a/tests/ui/consts/escaping-bound-var.rs b/tests/ui/consts/escaping-bound-var.rs index 7c1fbd24f555..a538d607d6ca 100644 --- a/tests/ui/consts/escaping-bound-var.rs +++ b/tests/ui/consts/escaping-bound-var.rs @@ -3,7 +3,7 @@ fn test<'a>( _: &'a (), -) -> [(); { +) -> [(); { //~ ERROR: mismatched types let x: &'a (); //~^ ERROR cannot capture late-bound lifetime in constant 1 diff --git a/tests/ui/consts/escaping-bound-var.stderr b/tests/ui/consts/escaping-bound-var.stderr index a943c84e3b2a..bb0d285f4d06 100644 --- a/tests/ui/consts/escaping-bound-var.stderr +++ b/tests/ui/consts/escaping-bound-var.stderr @@ -16,5 +16,23 @@ LL | fn test<'a>( LL | let x: &'a (); | ^^ -error: aborting due to 1 previous error; 1 warning emitted +error[E0308]: mismatched types + --> $DIR/escaping-bound-var.rs:6:6 + | +LL | fn test<'a>( + | ---- implicitly returns `()` as its body has no tail or `return` expression +LL | _: &'a (), +LL | ) -> [(); { + | ______^ +LL | | let x: &'a (); +LL | | +LL | | 1 +LL | | }] { + | |__^ expected `[(); { + let x: &'a (); + 1 +}]`, found `()` +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/issue-103790.rs b/tests/ui/consts/issue-103790.rs index ea3cac605b15..5d130821dc8e 100644 --- a/tests/ui/consts/issue-103790.rs +++ b/tests/ui/consts/issue-103790.rs @@ -6,5 +6,6 @@ struct S; //~| ERROR missing generics for struct `S` //~| ERROR cycle detected when computing type of `S::S` //~| ERROR cycle detected when computing type of `S` +//~| ERROR `()` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/consts/issue-103790.stderr b/tests/ui/consts/issue-103790.stderr index 67334469dcde..55bc9675401b 100644 --- a/tests/ui/consts/issue-103790.stderr +++ b/tests/ui/consts/issue-103790.stderr @@ -61,7 +61,16 @@ LL | | fn main() {} | |____________^ = 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: aborting due to 4 previous errors +error: `()` is forbidden as the type of a const generic parameter + --> $DIR/issue-103790.rs:4:19 + | +LL | struct S; + | ^^ + | + = note: the only supported types are integers, `bool` and `char` + = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types + +error: aborting due to 5 previous errors Some errors have detailed explanations: E0107, E0391, E0403. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/derives/issue-97343.rs b/tests/ui/derives/issue-97343.rs index 6f0e4d55aeb0..91f0aa376e9a 100644 --- a/tests/ui/derives/issue-97343.rs +++ b/tests/ui/derives/issue-97343.rs @@ -2,6 +2,7 @@ use std::fmt::Debug; #[derive(Debug)] pub struct Irrelevant { //~ ERROR type arguments are not allowed on type parameter + //~^ ERROR `Irrelevant` must be used irrelevant: Irrelevant, } diff --git a/tests/ui/derives/issue-97343.stderr b/tests/ui/derives/issue-97343.stderr index efb2fb70f5a5..45612ae6f474 100644 --- a/tests/ui/derives/issue-97343.stderr +++ b/tests/ui/derives/issue-97343.stderr @@ -16,6 +16,16 @@ LL | pub struct Irrelevant { | ^^^^^^^^^^ = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error +error[E0210]: type parameter `Irrelevant` must be used as the type parameter for some local type (e.g., `MyStruct`) + --> $DIR/issue-97343.rs:4:23 + | +LL | pub struct Irrelevant { + | ^^^^^^^^^^ type parameter `Irrelevant` must be used as the type parameter for some local type + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local + = note: only traits defined in the current crate can be implemented for a type parameter -For more information about this error, try `rustc --explain E0109`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0109, E0210. +For more information about an error, try `rustc --explain E0109`. diff --git a/tests/ui/did_you_mean/bad-assoc-ty.rs b/tests/ui/did_you_mean/bad-assoc-ty.rs index f787c416c2d6..5a559b01ea28 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.rs +++ b/tests/ui/did_you_mean/bad-assoc-ty.rs @@ -71,6 +71,7 @@ enum N where F: Fn() -> _ { union O where F: Fn() -> _ { //~^ ERROR the placeholder `_` is not allowed within types on item signatures for unions foo: F, + //~^ ERROR must implement `Copy` } trait P where F: Fn() -> _ { diff --git a/tests/ui/did_you_mean/bad-assoc-ty.stderr b/tests/ui/did_you_mean/bad-assoc-ty.stderr index 5c0c7a0b94ff..3c474d19d1d0 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.stderr +++ b/tests/ui/did_you_mean/bad-assoc-ty.stderr @@ -299,7 +299,7 @@ LL | union O where F: Fn() -> T { | +++ ~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for traits - --> $DIR/bad-assoc-ty.rs:76:29 + --> $DIR/bad-assoc-ty.rs:77:29 | LL | trait P where F: Fn() -> _ { | ^ not allowed in type signatures @@ -310,7 +310,7 @@ LL | trait P where F: Fn() -> T { | +++ ~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/bad-assoc-ty.rs:81:38 + --> $DIR/bad-assoc-ty.rs:82:38 | LL | fn foo(_: F) where F: Fn() -> _ {} | ^ not allowed in type signatures @@ -320,7 +320,19 @@ help: use type parameters instead LL | fn foo(_: F) where F: Fn() -> T {} | +++ ~ -error: aborting due to 28 previous errors; 1 warning emitted +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union + --> $DIR/bad-assoc-ty.rs:73:5 + | +LL | foo: F, + | ^^^^^^ + | + = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | foo: std::mem::ManuallyDrop, + | +++++++++++++++++++++++ + -Some errors have detailed explanations: E0121, E0223. +error: aborting due to 29 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0121, E0223, E0740. For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed index 4963790c35de..eebe8d6e3f33 100644 --- a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed +++ b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed @@ -8,6 +8,7 @@ trait Foo: Sized { impl Foo for () { fn bar(i: i32, t: usize, s: &()) -> (usize, i32) { //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~| ERROR type annotations needed (1, 2) } } diff --git a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs index ddf39c9c8619..aa7510821af9 100644 --- a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs +++ b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs @@ -8,6 +8,7 @@ trait Foo: Sized { impl Foo for () { fn bar(i: _, t: _, s: _) -> _ { //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~| ERROR type annotations needed (1, 2) } } diff --git a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr index 2ca6436bb999..6f38def69981 100644 --- a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr +++ b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr @@ -13,6 +13,13 @@ help: try replacing `_` with the types in the corresponding trait method signatu LL | fn bar(i: i32, t: usize, s: &()) -> (usize, i32) { | ~~~ ~~~~~ ~~~ ~~~~~~~~~~~~ -error: aborting due to 1 previous error +error[E0282]: type annotations needed + --> $DIR/replace-impl-infer-ty-from-trait.rs:9:12 + | +LL | fn bar(i: _, t: _, s: _) -> _ { + | ^ cannot infer type -For more information about this error, try `rustc --explain E0121`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0121, E0282. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/dyn-keyword/dyn-2021-edition-error.rs b/tests/ui/dyn-keyword/dyn-2021-edition-error.rs index 0f05d8753eaa..bc1bed8a9a4c 100644 --- a/tests/ui/dyn-keyword/dyn-2021-edition-error.rs +++ b/tests/ui/dyn-keyword/dyn-2021-edition-error.rs @@ -4,6 +4,7 @@ fn function(x: &SomeTrait, y: Box) { //~^ ERROR trait objects must include the `dyn` keyword //~| ERROR trait objects must include the `dyn` keyword let _x: &SomeTrait = todo!(); + //~^ ERROR trait objects must include the `dyn` keyword } 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 08ee77116f0b..b39689afd1ca 100644 --- a/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr +++ b/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr @@ -20,6 +20,17 @@ help: add `dyn` keyword before this trait LL | fn function(x: &SomeTrait, y: Box) { | +++ -error: aborting due to 2 previous errors +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/dyn-2021-edition-error.rs:6:14 + | +LL | let _x: &SomeTrait = todo!(); + | ^^^^^^^^^ + | +help: add `dyn` keyword before this trait + | +LL | let _x: &dyn SomeTrait = todo!(); + | +++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/error-codes/E0227.rs b/tests/ui/error-codes/E0227.rs index 0f0a781d2f9d..4dd4da55fa32 100644 --- a/tests/ui/error-codes/E0227.rs +++ b/tests/ui/error-codes/E0227.rs @@ -6,6 +6,7 @@ trait FooBar<'foo, 'bar>: Foo<'foo> + Bar<'bar> {} struct Baz<'foo, 'bar> { baz: dyn FooBar<'foo, 'bar>, //~^ ERROR ambiguous lifetime bound, explicit lifetime bound required + //~| ERROR lifetime bound not satisfied } fn main() { diff --git a/tests/ui/error-codes/E0227.stderr b/tests/ui/error-codes/E0227.stderr index c77a2e98af70..6338034a022a 100644 --- a/tests/ui/error-codes/E0227.stderr +++ b/tests/ui/error-codes/E0227.stderr @@ -4,6 +4,24 @@ error[E0227]: ambiguous lifetime bound, explicit lifetime bound required LL | baz: dyn FooBar<'foo, 'bar>, | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0478]: lifetime bound not satisfied + --> $DIR/E0227.rs:7:10 + | +LL | baz: dyn FooBar<'foo, 'bar>, + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: lifetime parameter instantiated with the lifetime `'bar` as defined here + --> $DIR/E0227.rs:6:18 + | +LL | struct Baz<'foo, 'bar> { + | ^^^^ +note: but lifetime parameter must outlive the lifetime `'foo` as defined here + --> $DIR/E0227.rs:6:12 + | +LL | struct Baz<'foo, 'bar> { + | ^^^^ -For more information about this error, try `rustc --explain E0227`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0227, E0478. +For more information about an error, try `rustc --explain E0227`. diff --git a/tests/ui/error-codes/E0229.rs b/tests/ui/error-codes/E0229.rs index 4c1934107a6d..b7cdb1737b00 100644 --- a/tests/ui/error-codes/E0229.rs +++ b/tests/ui/error-codes/E0229.rs @@ -12,6 +12,9 @@ impl Foo for isize { fn baz(x: &>::A) {} //~^ ERROR associated type bindings are not allowed here [E0229] +//~| ERROR associated type bindings are not allowed here [E0229] +//~| ERROR associated type bindings are not allowed here [E0229] +//~| ERROR the trait bound `I: Foo` is not satisfied fn main() { } diff --git a/tests/ui/error-codes/E0229.stderr b/tests/ui/error-codes/E0229.stderr index e981fb91ce89..a7a2904bb898 100644 --- a/tests/ui/error-codes/E0229.stderr +++ b/tests/ui/error-codes/E0229.stderr @@ -4,6 +4,34 @@ error[E0229]: associated type bindings are not allowed here LL | fn baz(x: &>::A) {} | ^^^^^ associated type not allowed here -error: aborting due to 1 previous error +error[E0229]: associated type bindings are not allowed here + --> $DIR/E0229.rs:13:25 + | +LL | fn baz(x: &>::A) {} + | ^^^^^ associated type not allowed here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -For more information about this error, try `rustc --explain E0229`. +error[E0229]: associated type bindings are not allowed here + --> $DIR/E0229.rs:13:25 + | +LL | fn baz(x: &>::A) {} + | ^^^^^ associated type not allowed here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `I: Foo` is not satisfied + --> $DIR/E0229.rs:13:15 + | +LL | fn baz(x: &>::A) {} + | ^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `I` + | +help: consider restricting type parameter `I` + | +LL | fn baz(x: &>::A) {} + | +++++ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0229, E0277. +For more information about an error, try `rustc --explain E0229`. diff --git a/tests/ui/error-codes/E0719.rs b/tests/ui/error-codes/E0719.rs index cbf1bb219a07..0ea6d19000bd 100644 --- a/tests/ui/error-codes/E0719.rs +++ b/tests/ui/error-codes/E0719.rs @@ -1,6 +1,7 @@ trait Foo: Iterator {} //~^ ERROR is already specified //~| ERROR is already specified +//~| ERROR is already specified type Unit = (); @@ -11,5 +12,6 @@ fn test() -> Box> { fn main() { let _: &dyn Iterator; + //~^ ERROR already specified test(); } diff --git a/tests/ui/error-codes/E0719.stderr b/tests/ui/error-codes/E0719.stderr index 00aea97139a7..f048a8aabd44 100644 --- a/tests/ui/error-codes/E0719.stderr +++ b/tests/ui/error-codes/E0719.stderr @@ -17,13 +17,31 @@ LL | trait Foo: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/E0719.rs:7:42 + --> $DIR/E0719.rs:8:42 | LL | fn test() -> Box> { | --------- ^^^^^^^^^^^ re-bound here | | | `Item` bound here first -error: aborting due to 3 previous errors +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/E0719.rs:1:33 + | +LL | trait Foo: Iterator {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/E0719.rs:14:38 + | +LL | let _: &dyn Iterator; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0719`. diff --git a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.rs b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.rs index de0487cdb208..2c130664e9a1 100644 --- a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.rs +++ b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.rs @@ -5,6 +5,7 @@ trait Foo { impl Foo for () { type Bar = impl std::fmt::Debug; //~^ ERROR: `impl Trait` in associated types is unstable + //~| ERROR: unconstrained opaque type } struct Mop; @@ -13,6 +14,7 @@ impl Mop { type Bop = impl std::fmt::Debug; //~^ ERROR: `impl Trait` in associated types is unstable //~| ERROR: inherent associated types are unstable + //~| ERROR: unconstrained opaque type } fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr index 9a1ded968225..420363ced6f7 100644 --- a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr +++ b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr @@ -8,7 +8,7 @@ LL | type Bar = impl std::fmt::Debug; = help: add `#![feature(impl_trait_in_assoc_type)]` to the crate attributes to enable error[E0658]: `impl Trait` in associated types is unstable - --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:13:16 + --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:14:16 | LL | type Bop = impl std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | type Bop = impl std::fmt::Debug; = help: add `#![feature(impl_trait_in_assoc_type)]` to the crate attributes to enable error[E0658]: inherent associated types are unstable - --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:13:5 + --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:14:5 | LL | type Bop = impl std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,6 +25,22 @@ LL | type Bop = impl std::fmt::Debug; = note: see issue #8995 for more information = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable -error: aborting due to 3 previous errors +error: unconstrained opaque type + --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:6:16 + | +LL | type Bar = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `Bar` must be used in combination with a concrete type within the same impl + +error: unconstrained opaque type + --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:14:16 + | +LL | type Bop = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `Bop` must be used in combination with a concrete type within the same impl + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs index eecf2046ccbe..b8ce9c85b727 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs @@ -7,29 +7,35 @@ struct Foo; impl Fn<()> for Foo { -//~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change -//~| ERROR manual implementations of `Fn` are experimental + //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change + //~| ERROR manual implementations of `Fn` are experimental + //~| ERROR expected a `FnMut()` closure, found `Foo` extern "rust-call" fn call(self, args: ()) -> () {} //~^ ERROR rust-call ABI is subject to change + //~| ERROR `call` has an incompatible type for trait } struct Foo1; impl FnOnce() for Foo1 { -//~^ ERROR associated type bindings are not allowed here -//~| ERROR manual implementations of `FnOnce` are experimental + //~^ ERROR associated type bindings are not allowed here + //~| ERROR manual implementations of `FnOnce` are experimental + //~| ERROR not all trait items implemented extern "rust-call" fn call_once(self, args: ()) -> () {} //~^ ERROR rust-call ABI is subject to change } struct Bar; impl FnMut<()> for Bar { -//~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change -//~| ERROR manual implementations of `FnMut` are experimental + //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change + //~| ERROR manual implementations of `FnMut` are experimental + //~| ERROR expected a `FnOnce()` closure, found `Bar` extern "rust-call" fn call_mut(&self, args: ()) -> () {} //~^ ERROR rust-call ABI is subject to change + //~| ERROR incompatible type for trait } struct Baz; impl FnOnce<()> for Baz { -//~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change -//~| ERROR manual implementations of `FnOnce` are experimental + //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change + //~| ERROR manual implementations of `FnOnce` are experimental + //~| ERROR not all trait items implemented extern "rust-call" fn call_once(&self, args: ()) -> () {} //~^ ERROR rust-call ABI is subject to change } diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr index b1613f638d30..b417dfb506ad 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr @@ -1,5 +1,5 @@ error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:12:12 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:13:12 | LL | extern "rust-call" fn call(self, args: ()) -> () {} | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | extern "rust-call" fn call(self, args: ()) -> () {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:19:12 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:22:12 | LL | extern "rust-call" fn call_once(self, args: ()) -> () {} | ^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | extern "rust-call" fn call_once(self, args: ()) -> () {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:26:12 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:30:12 | LL | extern "rust-call" fn call_mut(&self, args: ()) -> () {} | ^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | extern "rust-call" fn call_mut(&self, args: ()) -> () {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:33:12 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:39:12 | LL | extern "rust-call" fn call_once(&self, args: ()) -> () {} | ^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | impl Fn<()> for Foo { = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0183]: manual implementations of `FnOnce` are experimental - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:16:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:18:6 | LL | impl FnOnce() for Foo1 { | ^^^^^^^^ manual implementations of `FnOnce` are experimental @@ -60,19 +60,19 @@ LL | impl FnOnce() for Foo1 { = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0229]: associated type bindings are not allowed here - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:16:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:18:6 | LL | impl FnOnce() for Foo1 { | ^^^^^^^^ associated type not allowed here | help: parenthesized trait syntax expands to `FnOnce<(), Output=()>` - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:16:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:18:6 | LL | impl FnOnce() for Foo1 { | ^^^^^^^^ error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:23:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:26:6 | LL | impl FnMut<()> for Bar { | ^^^^^^^^^ @@ -81,7 +81,7 @@ LL | impl FnMut<()> for Bar { = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0183]: manual implementations of `FnMut` are experimental - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:23:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:26:6 | LL | impl FnMut<()> for Bar { | ^^^^^^^^^ manual implementations of `FnMut` are experimental @@ -89,7 +89,7 @@ LL | impl FnMut<()> for Bar { = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:30:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:35:6 | LL | impl FnOnce<()> for Baz { | ^^^^^^^^^^ @@ -98,14 +98,76 @@ LL | impl FnOnce<()> for Baz { = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0183]: manual implementations of `FnOnce` are experimental - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:30:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:35:6 | LL | impl FnOnce<()> for Baz { | ^^^^^^^^^^ manual implementations of `FnOnce` are experimental | = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error: aborting due to 12 previous errors +error[E0277]: expected a `FnMut()` closure, found `Foo` + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:9:17 + | +LL | impl Fn<()> for Foo { + | ^^^ expected an `FnMut()` closure, found `Foo` + | + = help: the trait `FnMut<()>` is not implemented for `Foo` + = note: wrap the `Foo` in a closure with no arguments: `|| { /* code */ }` +note: required by a bound in `Fn` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL -Some errors have detailed explanations: E0183, E0229, E0658. -For more information about an error, try `rustc --explain E0183`. +error[E0053]: method `call` has an incompatible type for trait + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:13:32 + | +LL | extern "rust-call" fn call(self, args: ()) -> () {} + | ^^^^ + | | + | expected `&Foo`, found `Foo` + | help: change the self-receiver type to match the trait: `&self` + | + = note: expected signature `extern "rust-call" fn(&Foo, ()) -> _` + found signature `extern "rust-call" fn(Foo, ())` + +error[E0046]: not all trait items implemented, missing: `Output` + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:18:1 + | +LL | impl FnOnce() for Foo1 { + | ^^^^^^^^^^^^^^^^^^^^^^ missing `Output` in implementation + | + = help: implement the missing item: `type Output = /* Type */;` + +error[E0277]: expected a `FnOnce()` closure, found `Bar` + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:26:20 + | +LL | impl FnMut<()> for Bar { + | ^^^ expected an `FnOnce()` closure, found `Bar` + | + = help: the trait `FnOnce<()>` is not implemented for `Bar` + = note: wrap the `Bar` in a closure with no arguments: `|| { /* code */ }` +note: required by a bound in `FnMut` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error[E0053]: method `call_mut` has an incompatible type for trait + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:30:36 + | +LL | extern "rust-call" fn call_mut(&self, args: ()) -> () {} + | ^^^^^ + | | + | types differ in mutability + | help: change the self-receiver type to match the trait: `&mut self` + | + = note: expected signature `extern "rust-call" fn(&mut Bar, ()) -> _` + found signature `extern "rust-call" fn(&Bar, ())` + +error[E0046]: not all trait items implemented, missing: `Output` + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:35:1 + | +LL | impl FnOnce<()> for Baz { + | ^^^^^^^^^^^^^^^^^^^^^^^ missing `Output` in implementation + | + = help: implement the missing item: `type Output = /* Type */;` + +error: aborting due to 18 previous errors + +Some errors have detailed explanations: E0046, E0053, E0183, E0229, E0277, E0658. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/fn/issue-39259.rs b/tests/ui/fn/issue-39259.rs index 5872f1007b01..16983b652fc8 100644 --- a/tests/ui/fn/issue-39259.rs +++ b/tests/ui/fn/issue-39259.rs @@ -4,8 +4,10 @@ struct S; impl Fn(u32) -> u32 for S { -//~^ ERROR associated type bindings are not allowed here [E0229] + //~^ ERROR associated type bindings are not allowed here [E0229] + //~| ERROR expected a `FnMut(u32)` closure, found `S` fn call(&self) -> u32 { + //~^ ERROR method `call` has 1 parameter but the declaration in trait `call` has 2 5 } } diff --git a/tests/ui/fn/issue-39259.stderr b/tests/ui/fn/issue-39259.stderr index bd102e581356..47150a3c155c 100644 --- a/tests/ui/fn/issue-39259.stderr +++ b/tests/ui/fn/issue-39259.stderr @@ -10,6 +10,25 @@ help: parenthesized trait syntax expands to `Fn<(u32,), Output=u32>` LL | impl Fn(u32) -> u32 for S { | ^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0277]: expected a `FnMut(u32)` closure, found `S` + --> $DIR/issue-39259.rs:6:25 + | +LL | impl Fn(u32) -> u32 for S { + | ^ expected an `FnMut(u32)` closure, found `S` + | + = help: the trait `FnMut<(u32,)>` is not implemented for `S` +note: required by a bound in `Fn` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL -For more information about this error, try `rustc --explain E0229`. +error[E0050]: method `call` has 1 parameter but the declaration in trait `call` has 2 + --> $DIR/issue-39259.rs:9:13 + | +LL | fn call(&self) -> u32 { + | ^^^^^ expected 2 parameters, found 1 + | + = note: `call` from trait: `extern "rust-call" fn(&Self, Args) -> >::Output` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0050, E0229, E0277. +For more information about an error, try `rustc --explain E0050`. diff --git a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.rs b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.rs index 86b164ba7d8a..1a4678c7e70b 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.rs +++ b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.rs @@ -6,4 +6,7 @@ fn main() { fn _f(arg : Box X = &'a [u32]>>) {} //~^ ERROR: use of undeclared lifetime name `'x` //~| ERROR: binding for associated type `Y` references lifetime + //~| ERROR: binding for associated type `Y` references lifetime + //~| ERROR: binding for associated type `Y` references lifetime + //~| ERROR: the trait `X` cannot be made into an object } diff --git a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr index b77f10084c92..4a56b20eb59b 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr +++ b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr @@ -20,7 +20,38 @@ error[E0582]: binding for associated type `Y` references lifetime `'a`, which do LL | fn _f(arg : Box X = &'a [u32]>>) {} | ^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error[E0582]: binding for associated type `Y` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/gat-in-trait-path-undeclared-lifetime.rs:6:33 + | +LL | fn _f(arg : Box X = &'a [u32]>>) {} + | ^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -Some errors have detailed explanations: E0261, E0582. -For more information about an error, try `rustc --explain E0261`. +error[E0582]: binding for associated type `Y` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/gat-in-trait-path-undeclared-lifetime.rs:6:33 + | +LL | fn _f(arg : Box X = &'a [u32]>>) {} + | ^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0038]: the trait `X` cannot be made into an object + --> $DIR/gat-in-trait-path-undeclared-lifetime.rs:6:19 + | +LL | fn _f(arg : Box X = &'a [u32]>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/gat-in-trait-path-undeclared-lifetime.rs:2:8 + | +LL | trait X { + | - this trait cannot be made into an object... +LL | type Y<'x>; + | ^ ...because it contains the generic associated type `Y` + = help: consider moving `Y` to another trait + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0038, E0261, E0582. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.rs b/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.rs index 83b86f04a957..285493132b64 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.rs +++ b/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.rs @@ -4,10 +4,11 @@ trait X { fn foo<'a>(t : Self::Y<'a>) -> Self::Y<'a> { t } } -impl X for T { +impl X for T { //~ ERROR: not all trait items implemented fn foo<'a, T1: X>(t : T1) -> T1::Y<'a> { //~^ ERROR missing generics for associated type //~^^ ERROR missing generics for associated type + //~| ERROR method `foo` has 1 type parameter but its trait declaration has 0 type parameters t } } diff --git a/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.stderr b/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.stderr index 7f535ec432c1..74ce93a613cf 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.stderr +++ b/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.stderr @@ -31,6 +31,27 @@ help: add missing lifetime argument LL | fn foo<'a, T1: X = T1>>(t : T1) -> T1::Y<'a> { | ++++ -error: aborting due to 2 previous errors +error[E0049]: method `foo` has 1 type parameter but its trait declaration has 0 type parameters + --> $DIR/gat-trait-path-missing-lifetime.rs:8:10 + | +LL | fn foo<'a>(t : Self::Y<'a>) -> Self::Y<'a> { t } + | -- expected 0 type parameters +... +LL | fn foo<'a, T1: X>(t : T1) -> T1::Y<'a> { + | ^^ ^^ + | | + | found 1 type parameter -For more information about this error, try `rustc --explain E0107`. +error[E0046]: not all trait items implemented, missing: `Y` + --> $DIR/gat-trait-path-missing-lifetime.rs:7:1 + | +LL | type Y<'a>; + | ---------- `Y` from trait +... +LL | impl X for T { + | ^^^^^^^^^^^^^^^ missing `Y` in implementation + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0046, E0049, E0107. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs index 5738dfa83eea..c41344270130 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs +++ b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs @@ -7,10 +7,19 @@ fn foo<'a>(arg: Box>) {} //~| ERROR: parenthesized generic arguments cannot be used //~| ERROR associated type takes 0 generic arguments but 1 generic argument //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR associated type takes 0 generic arguments but 1 generic argument + //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR associated type takes 0 generic arguments but 1 generic argument + //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR at least one trait is required + //~| ERROR: the trait `X` cannot be made into an object fn bar<'a>(arg: Box>) {} //~^ ERROR: parenthesized generic arguments cannot be used //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR: the trait `X` cannot be made into an object fn main() {} diff --git a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr index 461853379b56..bad2ae9c9183 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr +++ b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr @@ -16,7 +16,7 @@ LL | fn foo<'a>(arg: Box = &'a ()>>) {} | ~ ~ error: parenthesized generic arguments cannot be used in associated type constraints - --> $DIR/gat-trait-path-parenthesised-args.rs:12:27 + --> $DIR/gat-trait-path-parenthesised-args.rs:18:27 | LL | fn bar<'a>(arg: Box>) {} | ^-- @@ -54,7 +54,7 @@ LL | type Y<'a>; | ^ error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied - --> $DIR/gat-trait-path-parenthesised-args.rs:12:27 + --> $DIR/gat-trait-path-parenthesised-args.rs:18:27 | LL | fn bar<'a>(arg: Box>) {} | ^ expected 1 lifetime argument @@ -69,6 +69,141 @@ help: add missing lifetime argument LL | fn bar<'a>(arg: Box>) {} | ++ -error: aborting due to 6 previous errors +error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/gat-trait-path-parenthesised-args.rs:5:27 + | +LL | fn foo<'a>(arg: Box>) {} + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 + | +LL | type Y<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | fn foo<'a>(arg: Box>) {} + | +++ -For more information about this error, try `rustc --explain E0107`. +error[E0107]: associated type takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/gat-trait-path-parenthesised-args.rs:5:27 + | +LL | fn foo<'a>(arg: Box>) {} + | ^---- help: remove these generics + | | + | expected 0 generic arguments + | +note: associated type defined here, with 0 generic parameters + --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 + | +LL | type Y<'a>; + | ^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/gat-trait-path-parenthesised-args.rs:5:27 + | +LL | fn foo<'a>(arg: Box>) {} + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 + | +LL | type Y<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | fn foo<'a>(arg: Box>) {} + | +++ + +error[E0107]: associated type takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/gat-trait-path-parenthesised-args.rs:5:27 + | +LL | fn foo<'a>(arg: Box>) {} + | ^---- help: remove these generics + | | + | expected 0 generic arguments + | +note: associated type defined here, with 0 generic parameters + --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 + | +LL | type Y<'a>; + | ^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0224]: at least one trait is required for an object type + --> $DIR/gat-trait-path-parenthesised-args.rs:5:29 + | +LL | fn foo<'a>(arg: Box>) {} + | ^^ + +error[E0038]: the trait `X` cannot be made into an object + --> $DIR/gat-trait-path-parenthesised-args.rs:5:21 + | +LL | fn foo<'a>(arg: Box>) {} + | ^^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 + | +LL | trait X { + | - this trait cannot be made into an object... +LL | type Y<'a>; + | ^ ...because it contains the generic associated type `Y` + = help: consider moving `Y` to another trait + +error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/gat-trait-path-parenthesised-args.rs:18:27 + | +LL | fn bar<'a>(arg: Box>) {} + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 + | +LL | type Y<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | fn bar<'a>(arg: Box>) {} + | ++ + +error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/gat-trait-path-parenthesised-args.rs:18:27 + | +LL | fn bar<'a>(arg: Box>) {} + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 + | +LL | type Y<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | fn bar<'a>(arg: Box>) {} + | ++ + +error[E0038]: the trait `X` cannot be made into an object + --> $DIR/gat-trait-path-parenthesised-args.rs:18:21 + | +LL | fn bar<'a>(arg: Box>) {} + | ^^^^^^^^^^^^^^^ `X` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 + | +LL | trait X { + | - this trait cannot be made into an object... +LL | type Y<'a>; + | ^ ...because it contains the generic associated type `Y` + = help: consider moving `Y` to another trait + +error: aborting due to 15 previous errors + +Some errors have detailed explanations: E0038, E0107, E0224. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-71176.rs b/tests/ui/generic-associated-types/issue-71176.rs index f0e162d825f9..e58b6f6091e7 100644 --- a/tests/ui/generic-associated-types/issue-71176.rs +++ b/tests/ui/generic-associated-types/issue-71176.rs @@ -9,6 +9,9 @@ impl Provider for () { struct Holder { inner: Box>, //~^ ERROR: missing generics for associated type + //~| ERROR: missing generics for associated type + //~| ERROR: missing generics for associated type + //~| ERROR: the trait `Provider` cannot be made into an object } fn main() { diff --git a/tests/ui/generic-associated-types/issue-71176.stderr b/tests/ui/generic-associated-types/issue-71176.stderr index ed837f347533..a1913bb618bc 100644 --- a/tests/ui/generic-associated-types/issue-71176.stderr +++ b/tests/ui/generic-associated-types/issue-71176.stderr @@ -14,6 +14,57 @@ help: add missing lifetime argument LL | inner: Box = B>>, | ++++ -error: aborting due to 1 previous error +error[E0107]: missing generics for associated type `Provider::A` + --> $DIR/issue-71176.rs:10:27 + | +LL | inner: Box>, + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/issue-71176.rs:2:10 + | +LL | type A<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | inner: Box = B>>, + | ++++ -For more information about this error, try `rustc --explain E0107`. +error[E0107]: missing generics for associated type `Provider::A` + --> $DIR/issue-71176.rs:10:27 + | +LL | inner: Box>, + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/issue-71176.rs:2:10 + | +LL | type A<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | inner: Box = B>>, + | ++++ + +error[E0038]: the trait `Provider` cannot be made into an object + --> $DIR/issue-71176.rs:10:14 + | +LL | inner: Box>, + | ^^^^^^^^^^^^^^^^^^^ `Provider` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/issue-71176.rs:2:10 + | +LL | trait Provider { + | -------- this trait cannot be made into an object... +LL | type A<'a>; + | ^ ...because it contains the generic associated type `A` + = help: consider moving `A` to another trait + = help: only type `()` implements the trait, consider using it directly instead + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0038, E0107. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-79636-1.rs b/tests/ui/generic-associated-types/issue-79636-1.rs index a89039b5c720..a05311d59c11 100644 --- a/tests/ui/generic-associated-types/issue-79636-1.rs +++ b/tests/ui/generic-associated-types/issue-79636-1.rs @@ -3,6 +3,7 @@ trait Monad { type Wrapped; fn bind(self, f: F) -> Self::Wrapped { + //~^ ERROR: the size for values of type `Self` cannot be known todo!() } } @@ -14,8 +15,10 @@ where //~^ ERROR: missing generics for associated type `Monad::Wrapped` { outer.bind(|inner| inner) + //~^ ERROR type annotations needed } fn main() { assert_eq!(join(Some(Some(true))), Some(true)); + //~^ ERROR: `Option>: Monad` is not satisfied } diff --git a/tests/ui/generic-associated-types/issue-79636-1.stderr b/tests/ui/generic-associated-types/issue-79636-1.stderr index 4076e951875e..743d8b7d4627 100644 --- a/tests/ui/generic-associated-types/issue-79636-1.stderr +++ b/tests/ui/generic-associated-types/issue-79636-1.stderr @@ -1,5 +1,5 @@ error[E0107]: missing generics for associated type `Monad::Wrapped` - --> $DIR/issue-79636-1.rs:13:34 + --> $DIR/issue-79636-1.rs:14:34 | LL | MInner: Monad>, | ^^^^^^^ expected 1 generic argument @@ -14,6 +14,56 @@ help: add missing generic argument LL | MInner: Monad = MOuter::Wrapped>, | +++ -error: aborting due to 1 previous error +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/issue-79636-1.rs:5:19 + | +LL | fn bind(self, f: F) -> Self::Wrapped { + | ^^^^ doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider further restricting `Self` + | +LL | fn bind(self, f: F) -> Self::Wrapped where Self: Sized { + | +++++++++++++++++ +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn bind(&self, f: F) -> Self::Wrapped { + | + -For more information about this error, try `rustc --explain E0107`. +error[E0282]: type annotations needed + --> $DIR/issue-79636-1.rs:17:17 + | +LL | outer.bind(|inner| inner) + | ^^^^^ + | +help: consider giving this closure parameter an explicit type + | +LL | outer.bind(|inner: /* Type */| inner) + | ++++++++++++ + +error[E0277]: the trait bound `Option>: Monad` is not satisfied + --> $DIR/issue-79636-1.rs:22:21 + | +LL | assert_eq!(join(Some(Some(true))), Some(true)); + | ---- ^^^^^^^^^^^^^^^^ the trait `Monad` is not implemented for `Option>` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/issue-79636-1.rs:1:1 + | +LL | trait Monad { + | ^^^^^^^^^^^ +note: required by a bound in `join` + --> $DIR/issue-79636-1.rs:13:13 + | +LL | fn join(outer: MOuter) -> MOuter::Wrapped + | ---- required by a bound in this function +LL | where +LL | MOuter: Monad, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `join` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0107, E0277, E0282. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/issue-80433.rs b/tests/ui/generic-associated-types/issue-80433.rs index 05ff82fa7d5d..bdba78c2ccd2 100644 --- a/tests/ui/generic-associated-types/issue-80433.rs +++ b/tests/ui/generic-associated-types/issue-80433.rs @@ -4,7 +4,7 @@ struct E { } trait TestMut { - type Output<'a>; + type Output<'a>; //~ ERROR missing required bound fn test_mut<'a>(&'a mut self) -> Self::Output<'a>; } diff --git a/tests/ui/generic-associated-types/issue-80433.stderr b/tests/ui/generic-associated-types/issue-80433.stderr index 488fbeceddd1..ab1fb7944180 100644 --- a/tests/ui/generic-associated-types/issue-80433.stderr +++ b/tests/ui/generic-associated-types/issue-80433.stderr @@ -14,6 +14,17 @@ help: add missing lifetime argument LL | fn test_simpler<'a>(dst: &'a mut impl TestMut = &'a mut f32>) | ++++ -error: aborting due to 1 previous error +error: missing required bound on `Output` + --> $DIR/issue-80433.rs:7:5 + | +LL | type Output<'a>; + | ^^^^^^^^^^^^^^^- + | | + | help: add the required where clause: `where Self: 'a` + | + = note: this bound is currently required to ensure that impls have maximum flexibility + = note: we are soliciting feedback, see issue #87479 for more information + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/missing_lifetime_args.rs b/tests/ui/generic-associated-types/missing_lifetime_args.rs index 331511ba61a4..470db5412b28 100644 --- a/tests/ui/generic-associated-types/missing_lifetime_args.rs +++ b/tests/ui/generic-associated-types/missing_lifetime_args.rs @@ -10,6 +10,9 @@ struct Foo<'a, 'b, 'c> { fn foo<'c, 'd>(_arg: Box>) {} //~^ ERROR missing generics for associated type +//~| ERROR missing generics for associated type +//~| ERROR missing generics for associated type +//~| ERROR the trait `X` cannot be made into an object fn bar<'a, 'b, 'c>(_arg: Foo<'a, 'b>) {} //~^ ERROR struct takes 3 lifetime arguments but 2 lifetime diff --git a/tests/ui/generic-associated-types/missing_lifetime_args.stderr b/tests/ui/generic-associated-types/missing_lifetime_args.stderr index 1a7a2e787a1a..5980c60c51c6 100644 --- a/tests/ui/generic-associated-types/missing_lifetime_args.stderr +++ b/tests/ui/generic-associated-types/missing_lifetime_args.stderr @@ -15,7 +15,7 @@ LL | fn foo<'c, 'd>(_arg: Box = (&'c u32, &'d u32)>>) {} | ++++++++ error[E0107]: struct takes 3 lifetime arguments but 2 lifetime arguments were supplied - --> $DIR/missing_lifetime_args.rs:14:26 + --> $DIR/missing_lifetime_args.rs:17:26 | LL | fn bar<'a, 'b, 'c>(_arg: Foo<'a, 'b>) {} | ^^^ -- -- supplied 2 lifetime arguments @@ -33,7 +33,7 @@ LL | fn bar<'a, 'b, 'c>(_arg: Foo<'a, 'b, 'a>) {} | ++++ error[E0107]: struct takes 3 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing_lifetime_args.rs:17:16 + --> $DIR/missing_lifetime_args.rs:20:16 | LL | fn f<'a>(_arg: Foo<'a>) {} | ^^^ -- supplied 1 lifetime argument @@ -50,6 +50,56 @@ help: add missing lifetime arguments LL | fn f<'a>(_arg: Foo<'a, 'a, 'a>) {} | ++++++++ -error: aborting due to 3 previous errors +error[E0107]: missing generics for associated type `X::Y` + --> $DIR/missing_lifetime_args.rs:11:32 + | +LL | fn foo<'c, 'd>(_arg: Box>) {} + | ^ expected 2 lifetime arguments + | +note: associated type defined here, with 2 lifetime parameters: `'a`, `'b` + --> $DIR/missing_lifetime_args.rs:2:10 + | +LL | type Y<'a, 'b>; + | ^ -- -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime arguments + | +LL | fn foo<'c, 'd>(_arg: Box = (&'c u32, &'d u32)>>) {} + | ++++++++ -For more information about this error, try `rustc --explain E0107`. +error[E0107]: missing generics for associated type `X::Y` + --> $DIR/missing_lifetime_args.rs:11:32 + | +LL | fn foo<'c, 'd>(_arg: Box>) {} + | ^ expected 2 lifetime arguments + | +note: associated type defined here, with 2 lifetime parameters: `'a`, `'b` + --> $DIR/missing_lifetime_args.rs:2:10 + | +LL | type Y<'a, 'b>; + | ^ -- -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime arguments + | +LL | fn foo<'c, 'd>(_arg: Box = (&'c u32, &'d u32)>>) {} + | ++++++++ + +error[E0038]: the trait `X` cannot be made into an object + --> $DIR/missing_lifetime_args.rs:11:26 + | +LL | fn foo<'c, 'd>(_arg: Box>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/missing_lifetime_args.rs:2:10 + | +LL | trait X { + | - this trait cannot be made into an object... +LL | type Y<'a, 'b>; + | ^ ...because it contains the generic associated type `Y` + = help: consider moving `Y` to another trait + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0038, E0107. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.rs b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.rs index c58f9cf1dfc8..d6fc3df1026f 100644 --- a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.rs +++ b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.rs @@ -6,6 +6,11 @@ const _: () = { fn f2<'a>(arg : Box = &'a ()>>) {} //~^ ERROR associated type takes 1 lifetime argument but 0 lifetime arguments //~| ERROR associated type takes 0 generic arguments but 1 generic argument + //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR associated type takes 0 generic arguments but 1 generic argument + //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments + //~| ERROR associated type takes 0 generic arguments but 1 generic argument + //~| ERROR the trait `X` cannot be made into an object }; fn main() {} diff --git a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr index fab5b474d928..2090f75aed32 100644 --- a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr +++ b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr @@ -28,6 +28,86 @@ note: associated type defined here, with 0 generic parameters LL | type Y<'a>; | ^ -error: aborting due to 2 previous errors +error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/trait-path-type-error-once-implemented.rs:6:29 + | +LL | fn f2<'a>(arg : Box = &'a ()>>) {} + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/trait-path-type-error-once-implemented.rs:2:10 + | +LL | type Y<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | fn f2<'a>(arg : Box = &'a ()>>) {} + | +++ -For more information about this error, try `rustc --explain E0107`. +error[E0107]: associated type takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/trait-path-type-error-once-implemented.rs:6:29 + | +LL | fn f2<'a>(arg : Box = &'a ()>>) {} + | ^--- help: remove these generics + | | + | expected 0 generic arguments + | +note: associated type defined here, with 0 generic parameters + --> $DIR/trait-path-type-error-once-implemented.rs:2:10 + | +LL | type Y<'a>; + | ^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/trait-path-type-error-once-implemented.rs:6:29 + | +LL | fn f2<'a>(arg : Box = &'a ()>>) {} + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/trait-path-type-error-once-implemented.rs:2:10 + | +LL | type Y<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | fn f2<'a>(arg : Box = &'a ()>>) {} + | +++ + +error[E0107]: associated type takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/trait-path-type-error-once-implemented.rs:6:29 + | +LL | fn f2<'a>(arg : Box = &'a ()>>) {} + | ^--- help: remove these generics + | | + | expected 0 generic arguments + | +note: associated type defined here, with 0 generic parameters + --> $DIR/trait-path-type-error-once-implemented.rs:2:10 + | +LL | type Y<'a>; + | ^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0038]: the trait `X` cannot be made into an object + --> $DIR/trait-path-type-error-once-implemented.rs:6:23 + | +LL | fn f2<'a>(arg : Box = &'a ()>>) {} + | ^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/trait-path-type-error-once-implemented.rs:2:10 + | +LL | trait X { + | - this trait cannot be made into an object... +LL | type Y<'a>; + | ^ ...because it contains the generic associated type `Y` + = help: consider moving `Y` to another trait + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0038, E0107. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/type-param-defaults.rs b/tests/ui/generic-associated-types/type-param-defaults.rs index f034076b0109..a9c8c5c12d9e 100644 --- a/tests/ui/generic-associated-types/type-param-defaults.rs +++ b/tests/ui/generic-associated-types/type-param-defaults.rs @@ -29,6 +29,8 @@ where fn main() { // errors foo::<()>(); + //~^ ERROR type mismatch + //~| ERROR `u64: Other` is not satisfied // works foo::(); } diff --git a/tests/ui/generic-associated-types/type-param-defaults.stderr b/tests/ui/generic-associated-types/type-param-defaults.stderr index 85ccaba0e69e..3c094d45fffc 100644 --- a/tests/ui/generic-associated-types/type-param-defaults.stderr +++ b/tests/ui/generic-associated-types/type-param-defaults.stderr @@ -16,5 +16,43 @@ error: defaults for type parameters are only allowed in `struct`, `enum`, `type` LL | type Assoc = T; | ^^^^^^^ -error: aborting due to 3 previous errors +error[E0271]: type mismatch resolving `<() as Trait>::Assoc == u32` + --> $DIR/type-param-defaults.rs:31:11 + | +LL | foo::<()>(); + | ^^ type mismatch resolving `<() as Trait>::Assoc == u32` + | +note: expected this to be `u32` + --> $DIR/type-param-defaults.rs:11:27 + | +LL | type Assoc = u64; + | ^^^ +note: required by a bound in `foo` + --> $DIR/type-param-defaults.rs:25:14 + | +LL | fn foo() + | --- required by a bound in this function +LL | where +LL | T: Trait, + | ^^^^^^^^^^^ required by this bound in `foo` +error[E0277]: the trait bound `u64: Other` is not satisfied + --> $DIR/type-param-defaults.rs:31:11 + | +LL | foo::<()>(); + | ^^ the trait `Other` is not implemented for `u64` + | + = help: the trait `Other` is implemented for `u32` +note: required by a bound in `foo` + --> $DIR/type-param-defaults.rs:26:15 + | +LL | fn foo() + | --- required by a bound in this function +... +LL | T::Assoc: Other { + | ^^^^^ required by this bound in `foo` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0271, E0277. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/generic-const-items/parameter-defaults.rs b/tests/ui/generic-const-items/parameter-defaults.rs index a6f82c249feb..c933db17fa20 100644 --- a/tests/ui/generic-const-items/parameter-defaults.rs +++ b/tests/ui/generic-const-items/parameter-defaults.rs @@ -11,4 +11,5 @@ const NONE: Option = None::; //~ ERROR defaults for type parameter fn main() { let _ = NONE; + //~^ ERROR type annotations needed } diff --git a/tests/ui/generic-const-items/parameter-defaults.stderr b/tests/ui/generic-const-items/parameter-defaults.stderr index 598622869454..697423e8dc39 100644 --- a/tests/ui/generic-const-items/parameter-defaults.stderr +++ b/tests/ui/generic-const-items/parameter-defaults.stderr @@ -4,5 +4,17 @@ error: defaults for type parameters are only allowed in `struct`, `enum`, `type` LL | const NONE: Option = None::; | ^^^^^^ -error: aborting due to 1 previous error +error[E0282]: type annotations needed for `Option` + --> $DIR/parameter-defaults.rs:13:9 + | +LL | let _ = NONE; + | ^ + | +help: consider giving this pattern a type, where the type for type parameter `T` is specified + | +LL | let _: Option = NONE; + | +++++++++++ +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/generics/wrong-number-of-args.rs b/tests/ui/generics/wrong-number-of-args.rs index e4eaff21af1d..95463d1c32c3 100644 --- a/tests/ui/generics/wrong-number-of-args.rs +++ b/tests/ui/generics/wrong-number-of-args.rs @@ -21,7 +21,7 @@ mod no_generics { } mod type_and_type { - struct Ty; + struct Ty(A, B); type A = Ty; //~^ ERROR missing generics for struct `type_and_type::Ty` @@ -43,7 +43,7 @@ mod type_and_type { } mod lifetime_and_type { - struct Ty<'a, T>; + struct Ty<'a, T>(&'a T); type A = Ty; //~^ ERROR missing generics for struct @@ -75,7 +75,7 @@ mod lifetime_and_type { } mod type_and_type_and_type { - struct Ty; + struct Ty(A, B, C); type A = Ty; //~^ ERROR missing generics for struct `type_and_type_and_type::Ty` diff --git a/tests/ui/generics/wrong-number-of-args.stderr b/tests/ui/generics/wrong-number-of-args.stderr index 9006fb10b67f..e04408a0fdf3 100644 --- a/tests/ui/generics/wrong-number-of-args.stderr +++ b/tests/ui/generics/wrong-number-of-args.stderr @@ -246,7 +246,7 @@ LL | type A = Ty; note: struct defined here, with 2 generic parameters: `A`, `B` --> $DIR/wrong-number-of-args.rs:24:12 | -LL | struct Ty; +LL | struct Ty(A, B); | ^^ - - help: add missing generic arguments | @@ -264,7 +264,7 @@ LL | type B = Ty; note: struct defined here, with 2 generic parameters: `A`, `B` --> $DIR/wrong-number-of-args.rs:24:12 | -LL | struct Ty; +LL | struct Ty(A, B); | ^^ - - help: add missing generic argument | @@ -282,7 +282,7 @@ LL | type D = Ty; note: struct defined here, with 2 generic parameters: `A`, `B` --> $DIR/wrong-number-of-args.rs:24:12 | -LL | struct Ty; +LL | struct Ty(A, B); | ^^ - - error[E0107]: struct takes 2 generic arguments but 0 generic arguments were supplied @@ -294,7 +294,7 @@ LL | type E = Ty<>; note: struct defined here, with 2 generic parameters: `A`, `B` --> $DIR/wrong-number-of-args.rs:24:12 | -LL | struct Ty; +LL | struct Ty(A, B); | ^^ - - help: add missing generic arguments | @@ -310,7 +310,7 @@ LL | type A = Ty; note: struct defined here, with 1 generic parameter: `T` --> $DIR/wrong-number-of-args.rs:46:12 | -LL | struct Ty<'a, T>; +LL | struct Ty<'a, T>(&'a T); | ^^ - help: add missing generic argument | @@ -326,7 +326,7 @@ LL | type B = Ty<'static>; note: struct defined here, with 1 generic parameter: `T` --> $DIR/wrong-number-of-args.rs:46:12 | -LL | struct Ty<'a, T>; +LL | struct Ty<'a, T>(&'a T); | ^^ - help: add missing generic argument | @@ -342,7 +342,7 @@ LL | type E = Ty<>; note: struct defined here, with 1 generic parameter: `T` --> $DIR/wrong-number-of-args.rs:46:12 | -LL | struct Ty<'a, T>; +LL | struct Ty<'a, T>(&'a T); | ^^ - help: add missing generic argument | @@ -360,7 +360,7 @@ LL | type F = Ty<'static, usize, 'static, usize>; note: struct defined here, with 1 lifetime parameter: `'a` --> $DIR/wrong-number-of-args.rs:46:12 | -LL | struct Ty<'a, T>; +LL | struct Ty<'a, T>(&'a T); | ^^ -- error[E0107]: struct takes 1 generic argument but 2 generic arguments were supplied @@ -374,7 +374,7 @@ LL | type F = Ty<'static, usize, 'static, usize>; note: struct defined here, with 1 generic parameter: `T` --> $DIR/wrong-number-of-args.rs:46:12 | -LL | struct Ty<'a, T>; +LL | struct Ty<'a, T>(&'a T); | ^^ - error[E0107]: missing generics for struct `type_and_type_and_type::Ty` @@ -386,7 +386,7 @@ LL | type A = Ty; note: struct defined here, with at least 2 generic parameters: `A`, `B` --> $DIR/wrong-number-of-args.rs:78:12 | -LL | struct Ty; +LL | struct Ty(A, B, C); | ^^ - - help: add missing generic arguments | @@ -404,7 +404,7 @@ LL | type B = Ty; note: struct defined here, with at least 2 generic parameters: `A`, `B` --> $DIR/wrong-number-of-args.rs:78:12 | -LL | struct Ty; +LL | struct Ty(A, B, C); | ^^ - - help: add missing generic argument | @@ -422,7 +422,7 @@ LL | type E = Ty; note: struct defined here, with at most 3 generic parameters: `A`, `B`, `C` --> $DIR/wrong-number-of-args.rs:78:12 | -LL | struct Ty; +LL | struct Ty(A, B, C); | ^^ - - ---------------- error[E0107]: struct takes at least 2 generic arguments but 0 generic arguments were supplied @@ -434,7 +434,7 @@ LL | type F = Ty<>; note: struct defined here, with at least 2 generic parameters: `A`, `B` --> $DIR/wrong-number-of-args.rs:78:12 | -LL | struct Ty; +LL | struct Ty(A, B, C); | ^^ - - help: add missing generic arguments | 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 2e13ca753fc2..7917fa991ee3 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,3 +1,9 @@ +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 { + | ^^^^^^^^^^^^^^^^^^^ the trait `AsRef<(dyn for<'a> Fn(&'a ()) + 'static)>` is not implemented for `()` + error[E0782]: trait objects must include the `dyn` keyword --> $DIR/generic-with-implicit-hrtb-without-dyn.rs:6:24 | @@ -9,6 +15,7 @@ help: add `dyn` keyword before this trait LL | fn ice() -> impl AsRef { | +++ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0782`. +Some errors have detailed explanations: E0277, E0782. +For more information about an error, try `rustc --explain E0277`. 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 bed81c4bca76..55d69069afb5 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 @@ -6,6 +6,7 @@ fn ice() -> impl AsRef { //[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] todo!() } diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs b/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs index 527a4586fd7e..06c3d9ad434e 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs @@ -4,16 +4,19 @@ use std::fmt::Debug; fn a() -> impl Fn(&u8) -> (impl Debug + '_) { //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet |x| x + //~^ ERROR lifetime may not live long enough } fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) { //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet |x| x + //~^ ERROR lifetime may not live long enough } fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet |x| x + //~^ ERROR lifetime may not live long enough } fn d() -> impl Fn() -> (impl Debug + '_) { diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr index a5982a5542a5..ebab99404931 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifier - --> $DIR/impl-fn-hrtb-bounds.rs:19:38 + --> $DIR/impl-fn-hrtb-bounds.rs:22:38 | LL | fn d() -> impl Fn() -> (impl Debug + '_) { | ^^ expected named lifetime parameter @@ -23,29 +23,56 @@ LL | fn a() -> impl Fn(&u8) -> (impl Debug + '_) { | ^ error: higher kinded lifetime bounds on nested opaque types are not supported yet - --> $DIR/impl-fn-hrtb-bounds.rs:9:52 + --> $DIR/impl-fn-hrtb-bounds.rs:10:52 | LL | fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) { | ^^ | note: lifetime declared here - --> $DIR/impl-fn-hrtb-bounds.rs:9:20 + --> $DIR/impl-fn-hrtb-bounds.rs:10:20 | LL | fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) { | ^^ error: higher kinded lifetime bounds on nested opaque types are not supported yet - --> $DIR/impl-fn-hrtb-bounds.rs:14:52 + --> $DIR/impl-fn-hrtb-bounds.rs:16:52 | LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { | ^^ | note: lifetime declared here - --> $DIR/impl-fn-hrtb-bounds.rs:14:20 + --> $DIR/impl-fn-hrtb-bounds.rs:16:20 | LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { | ^^ -error: aborting due to 4 previous errors +error: lifetime may not live long enough + --> $DIR/impl-fn-hrtb-bounds.rs:6:9 + | +LL | |x| x + | -- ^ returning this value requires that `'1` must outlive `'2` + | || + | |return type of closure is impl Debug + '2 + | has type `&'1 u8` + +error: lifetime may not live long enough + --> $DIR/impl-fn-hrtb-bounds.rs:12:9 + | +LL | |x| x + | -- ^ returning this value requires that `'1` must outlive `'2` + | || + | |return type of closure is impl Debug + '2 + | has type `&'1 u8` + +error: lifetime may not live long enough + --> $DIR/impl-fn-hrtb-bounds.rs:18:9 + | +LL | |x| x + | -- ^ returning this value requires that `'1` must outlive `'2` + | || + | |return type of closure is impl Debug + '2 + | has type `&'1 u8` + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/impl-trait/impl-fn-parsing-ambiguities.rs b/tests/ui/impl-trait/impl-fn-parsing-ambiguities.rs index 61303a5b2cb4..a4a1f1dcee12 100644 --- a/tests/ui/impl-trait/impl-fn-parsing-ambiguities.rs +++ b/tests/ui/impl-trait/impl-fn-parsing-ambiguities.rs @@ -5,6 +5,7 @@ fn a() -> impl Fn(&u8) -> impl Debug + '_ { //~^ ERROR ambiguous `+` in a type //~| ERROR higher kinded lifetime bounds on nested opaque types are not supported yet |x| x + //~^ ERROR lifetime may not live long enough } fn b() -> impl Fn() -> impl Debug + Send { diff --git a/tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr b/tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr index cf6e5ef7bace..e18e89700b4e 100644 --- a/tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr +++ b/tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr @@ -5,7 +5,7 @@ LL | fn a() -> impl Fn(&u8) -> impl Debug + '_ { | ^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(impl Debug + '_)` error: ambiguous `+` in a type - --> $DIR/impl-fn-parsing-ambiguities.rs:10:24 + --> $DIR/impl-fn-parsing-ambiguities.rs:11:24 | LL | fn b() -> impl Fn() -> impl Debug + Send { | ^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(impl Debug + Send)` @@ -22,5 +22,14 @@ note: lifetime declared here LL | fn a() -> impl Fn(&u8) -> impl Debug + '_ { | ^ -error: aborting due to 3 previous errors +error: lifetime may not live long enough + --> $DIR/impl-fn-parsing-ambiguities.rs:7:9 + | +LL | |x| x + | -- ^ returning this value requires that `'1` must outlive `'2` + | || + | |return type of closure is impl Debug + '2 + | has type `&'1 u8` + +error: aborting due to 4 previous errors diff --git a/tests/ui/impl-trait/impl_trait_projections.rs b/tests/ui/impl-trait/impl_trait_projections.rs index b3ff2ce5a7bf..365ac85e2f66 100644 --- a/tests/ui/impl-trait/impl_trait_projections.rs +++ b/tests/ui/impl-trait/impl_trait_projections.rs @@ -15,7 +15,7 @@ fn projection_is_disallowed(x: impl Iterator) -> ::Item { x.next().unwrap() } -fn projection_with_named_trait_is_disallowed(x: impl Iterator) +fn projection_with_named_trait_is_disallowed(mut x: impl Iterator) -> ::Item //~^ ERROR `impl Trait` is not allowed in path parameters { @@ -25,7 +25,9 @@ fn projection_with_named_trait_is_disallowed(x: impl Iterator) fn projection_with_named_trait_inside_path_is_disallowed() -> <::std::ops::Range as Iterator>::Item //~^ ERROR `impl Trait` is not allowed in path parameters +//~| ERROR `impl Debug: Step` is not satisfied { + //~^ ERROR `impl Debug: Step` is not satisfied (1i32..100).next().unwrap() } diff --git a/tests/ui/impl-trait/impl_trait_projections.stderr b/tests/ui/impl-trait/impl_trait_projections.stderr index 4deb24731bc0..700aff36aa50 100644 --- a/tests/ui/impl-trait/impl_trait_projections.stderr +++ b/tests/ui/impl-trait/impl_trait_projections.stderr @@ -17,7 +17,7 @@ LL | -> <::std::ops::Range as Iterator>::Item | ^^^^^^^^^^ error[E0667]: `impl Trait` is not allowed in path parameters - --> $DIR/impl_trait_projections.rs:33:29 + --> $DIR/impl_trait_projections.rs:35:29 | LL | -> as Iterator>::Item | ^^^^^^^^^^ @@ -28,6 +28,46 @@ error[E0667]: `impl Trait` is not allowed in path parameters LL | fn projection_is_disallowed(x: impl Iterator) -> ::Item { | ^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error[E0277]: the trait bound `impl Debug: Step` is not satisfied + --> $DIR/impl_trait_projections.rs:26:8 + | +LL | -> <::std::ops::Range as Iterator>::Item + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Step` is not implemented for `impl Debug` + | + = help: the following other types implement trait `Step`: + char + isize + i8 + i16 + i32 + i64 + i128 + usize + and 8 others + = note: required for `std::ops::Range` to implement `Iterator` -For more information about this error, try `rustc --explain E0667`. +error[E0277]: the trait bound `impl Debug: Step` is not satisfied + --> $DIR/impl_trait_projections.rs:29:1 + | +LL | / { +LL | | +LL | | (1i32..100).next().unwrap() +LL | | } + | |_^ the trait `Step` is not implemented for `impl Debug` + | + = help: the following other types implement trait `Step`: + char + isize + i8 + i16 + i32 + i64 + i128 + usize + and 8 others + = note: required for `std::ops::Range` to implement `Iterator` + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0277, E0667. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/implicit-capture-late.stderr b/tests/ui/impl-trait/implicit-capture-late.stderr index 9b3a4ff5f425..2fb5ebb65418 100644 --- a/tests/ui/impl-trait/implicit-capture-late.stderr +++ b/tests/ui/impl-trait/implicit-capture-late.stderr @@ -4,6 +4,12 @@ error[E0657]: `impl Trait` can only capture lifetimes bound at the fn or impl le LL | fn foo(x: Vec) -> Box Deref> { | ^^ -error: aborting due to 1 previous error +error: [o] + --> $DIR/implicit-capture-late.rs:10:55 + | +LL | fn foo(x: Vec) -> Box Deref> { + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0657`. diff --git a/tests/ui/impl-trait/issues/issue-67830.rs b/tests/ui/impl-trait/issues/issue-67830.rs index 92f7e005dbf0..6dc8935c7770 100644 --- a/tests/ui/impl-trait/issues/issue-67830.rs +++ b/tests/ui/impl-trait/issues/issue-67830.rs @@ -21,6 +21,8 @@ struct A; fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> { //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet Wrap(|a| Some(a).into_iter()) + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough } fn main() {} diff --git a/tests/ui/impl-trait/issues/issue-67830.stderr b/tests/ui/impl-trait/issues/issue-67830.stderr index 17cfa151a686..546198b8a103 100644 --- a/tests/ui/impl-trait/issues/issue-67830.stderr +++ b/tests/ui/impl-trait/issues/issue-67830.stderr @@ -10,5 +10,24 @@ note: lifetime declared here LL | fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> { | ^^ -error: aborting due to 1 previous error +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-67830.rs:23:5 + | +LL | Wrap(|a| Some(a).into_iter()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 A) -> std::option::IntoIter<&A>` must implement `FnOnce<(&'1 A,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 A,)>`, for some specific lifetime `'2` + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-67830.rs:23:5 + | +LL | Wrap(|a| Some(a).into_iter()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 A) -> std::option::IntoIter<&A>` must implement `FnOnce<(&'1 A,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 A,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors diff --git a/tests/ui/impl-trait/issues/issue-88236-2.rs b/tests/ui/impl-trait/issues/issue-88236-2.rs index fde8a6704cc4..f4354d1b2aef 100644 --- a/tests/ui/impl-trait/issues/issue-88236-2.rs +++ b/tests/ui/impl-trait/issues/issue-88236-2.rs @@ -18,11 +18,16 @@ fn make_impl() -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {} fn make_weird_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet &() + //~^ ERROR implementation of `Hrtb` is not general enough + //~| ERROR implementation of `Hrtb` is not general enough } fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet x + //~^ ERROR implementation of `Hrtb` is not general enough + //~| ERROR implementation of `Hrtb` is not general enough + //~| ERROR lifetime may not live long enough } fn main() {} diff --git a/tests/ui/impl-trait/issues/issue-88236-2.stderr b/tests/ui/impl-trait/issues/issue-88236-2.stderr index 8605d07abe94..1e63338d6d19 100644 --- a/tests/ui/impl-trait/issues/issue-88236-2.stderr +++ b/tests/ui/impl-trait/issues/issue-88236-2.stderr @@ -23,16 +23,70 @@ LL | fn make_weird_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Sen | ^^ error: higher kinded lifetime bounds on nested opaque types are not supported yet - --> $DIR/issue-88236-2.rs:23:78 + --> $DIR/issue-88236-2.rs:25:78 | LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { | ^^ | note: lifetime declared here - --> $DIR/issue-88236-2.rs:23:45 + --> $DIR/issue-88236-2.rs:25:45 | LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { | ^^ -error: aborting due to 3 previous errors +error: implementation of `Hrtb` is not general enough + --> $DIR/issue-88236-2.rs:20:5 + | +LL | &() + | ^^^ implementation of `Hrtb` is not general enough + | + = note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`... + = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1` + +error: implementation of `Hrtb` is not general enough + --> $DIR/issue-88236-2.rs:20:5 + | +LL | &() + | ^^^ implementation of `Hrtb` is not general enough + | + = note: `Hrtb<'a>` would have to be implemented for the type `&()` + = note: ...but `Hrtb<'0>` is actually implemented for the type `&'0 ()`, for some specific lifetime `'0` + +error: lifetime may not live long enough + --> $DIR/issue-88236-2.rs:27:5 + | +LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { + | -- lifetime `'b` defined here +LL | +LL | x + | ^ returning this value requires that `'b` must outlive `'static` + | +help: to declare that `impl for<'a> Hrtb<'a, Assoc = impl Send + '_>` captures data from argument `x`, you can add an explicit `'b` lifetime bound + | +LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b { + | ++++ +help: to declare that `impl Send + 'a` captures data from argument `x`, you can add an explicit `'b` lifetime bound + | +LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a + 'b> { + | ++++ + +error: implementation of `Hrtb` is not general enough + --> $DIR/issue-88236-2.rs:27:5 + | +LL | x + | ^ implementation of `Hrtb` is not general enough + | + = note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`... + = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1` + +error: implementation of `Hrtb` is not general enough + --> $DIR/issue-88236-2.rs:27:5 + | +LL | x + | ^ implementation of `Hrtb` is not general enough + | + = note: `Hrtb<'a>` would have to be implemented for the type `&()` + = note: ...but `Hrtb<'0>` is actually implemented for the type `&'0 ()`, for some specific lifetime `'0` + +error: aborting due to 8 previous errors diff --git a/tests/ui/impl-trait/issues/issue-92305.rs b/tests/ui/impl-trait/issues/issue-92305.rs index 4a89238d07e6..e16199caaaa2 100644 --- a/tests/ui/impl-trait/issues/issue-92305.rs +++ b/tests/ui/impl-trait/issues/issue-92305.rs @@ -5,6 +5,7 @@ use std::iter; fn f(data: &[T]) -> impl Iterator { //~^ ERROR: missing generics for struct `Vec` [E0107] iter::empty() + //~^ ERROR: type annotations needed } fn g(data: &[T], target: T) -> impl Iterator> { diff --git a/tests/ui/impl-trait/issues/issue-92305.stderr b/tests/ui/impl-trait/issues/issue-92305.stderr index 88fb1fb27070..55e966bd7bfd 100644 --- a/tests/ui/impl-trait/issues/issue-92305.stderr +++ b/tests/ui/impl-trait/issues/issue-92305.stderr @@ -9,6 +9,18 @@ help: add missing generic argument LL | fn f(data: &[T]) -> impl Iterator> { | +++ -error: aborting due to 1 previous error +error[E0282]: type annotations needed + --> $DIR/issue-92305.rs:7:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ -For more information about this error, try `rustc --explain E0107`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0107, E0282. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.rs b/tests/ui/impl-trait/nested-rpit-hrtb.rs index a5db10d3a220..a3eca741daad 100644 --- a/tests/ui/impl-trait/nested-rpit-hrtb.rs +++ b/tests/ui/impl-trait/nested-rpit-hrtb.rs @@ -31,9 +31,11 @@ fn one_hrtb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl Qux<'a>> {} fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a> {} //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet +//~| ERROR implementation of `Bar` is not general enough fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet +//~| ERROR: the trait bound `for<'a> &'a (): Qux<'_>` is not satisfied // This should resolve. fn one_hrtb_mention_fn_trait_param<'b>() -> impl for<'a> Foo<'a, Assoc = impl Qux<'b>> {} @@ -43,9 +45,11 @@ fn one_hrtb_mention_fn_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl Sized // This should resolve. fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Qux<'b>> {} +//~^ ERROR: the trait bound `for<'a> &'a (): Qux<'b>` is not satisfied // This should resolve. fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {} +//~^ ERROR implementation of `Bar` is not general enough // This should resolve. fn two_htrb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Qux<'b>> {} @@ -56,9 +60,11 @@ fn two_htrb_outlives() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> // This should resolve. fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux<'b>> {} +//~^ ERROR: the trait bound `for<'a, 'b> &'a (): Qux<'b>` is not satisfied // `'b` is not in scope for the outlives bound. fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {} //~^ ERROR use of undeclared lifetime name `'b` [E0261] +//~| ERROR implementation of `Bar` is not general enough fn main() {} diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.stderr b/tests/ui/impl-trait/nested-rpit-hrtb.stderr index 3dbe6ebadfbf..0e0f76874e3a 100644 --- a/tests/ui/impl-trait/nested-rpit-hrtb.stderr +++ b/tests/ui/impl-trait/nested-rpit-hrtb.stderr @@ -1,5 +1,5 @@ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/nested-rpit-hrtb.rs:54:77 + --> $DIR/nested-rpit-hrtb.rs:58:77 | LL | fn two_htrb_outlives() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {} | ^^ undeclared lifetime @@ -15,7 +15,7 @@ LL | fn two_htrb_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Siz | ++++ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/nested-rpit-hrtb.rs:61:82 + --> $DIR/nested-rpit-hrtb.rs:66:82 | LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {} | ^^ undeclared lifetime @@ -66,17 +66,72 @@ LL | fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a | ^^ error: higher kinded lifetime bounds on nested opaque types are not supported yet - --> $DIR/nested-rpit-hrtb.rs:35:73 + --> $DIR/nested-rpit-hrtb.rs:36:73 | LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} | ^^ | note: lifetime declared here - --> $DIR/nested-rpit-hrtb.rs:35:44 + --> $DIR/nested-rpit-hrtb.rs:36:44 | LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} | ^^ -error: aborting due to 6 previous errors +error: implementation of `Bar` is not general enough + --> $DIR/nested-rpit-hrtb.rs:32:78 + | +LL | fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a> {} + | ^^ implementation of `Bar` is not general enough + | + = note: `()` must implement `Bar<'a>` + = note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0` -For more information about this error, try `rustc --explain E0261`. +error[E0277]: the trait bound `for<'a> &'a (): Qux<'_>` is not satisfied + --> $DIR/nested-rpit-hrtb.rs:36:64 + | +LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} + | ^^^^^^^^^^^^ the trait `for<'a> Qux<'_>` is not implemented for `&'a ()` + | + = help: the trait `Qux<'_>` is implemented for `()` + = help: for that trait implementation, expected `()`, found `&'a ()` + +error[E0277]: the trait bound `for<'a> &'a (): Qux<'b>` is not satisfied + --> $DIR/nested-rpit-hrtb.rs:47:79 + | +LL | fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Qux<'b>> {} + | ^^^^^^^^^^^^ the trait `for<'a> Qux<'b>` is not implemented for `&'a ()` + | + = help: the trait `Qux<'_>` is implemented for `()` + = help: for that trait implementation, expected `()`, found `&'a ()` + +error: implementation of `Bar` is not general enough + --> $DIR/nested-rpit-hrtb.rs:51:93 + | +LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {} + | ^^ implementation of `Bar` is not general enough + | + = note: `()` must implement `Bar<'a>` + = note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0` + +error[E0277]: the trait bound `for<'a, 'b> &'a (): Qux<'b>` is not satisfied + --> $DIR/nested-rpit-hrtb.rs:62:64 + | +LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux<'b>> {} + | ^^^^^^^^^^^^^^^^^^^^ the trait `for<'a, 'b> Qux<'b>` is not implemented for `&'a ()` + | + = help: the trait `Qux<'_>` is implemented for `()` + = help: for that trait implementation, expected `()`, found `&'a ()` + +error: implementation of `Bar` is not general enough + --> $DIR/nested-rpit-hrtb.rs:66:86 + | +LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {} + | ^^ implementation of `Bar` is not general enough + | + = note: `()` must implement `Bar<'a>` + = note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0` + +error: aborting due to 12 previous errors + +Some errors have detailed explanations: E0261, E0277. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/intrinsics/safe-intrinsic-mismatch.rs b/tests/ui/intrinsics/safe-intrinsic-mismatch.rs index 50e12eaeb5cc..b0688e530ae7 100644 --- a/tests/ui/intrinsics/safe-intrinsic-mismatch.rs +++ b/tests/ui/intrinsics/safe-intrinsic-mismatch.rs @@ -3,9 +3,11 @@ extern "rust-intrinsic" { fn size_of() -> usize; //~ ERROR intrinsic safety mismatch + //~^ ERROR intrinsic safety mismatch #[rustc_safe_intrinsic] fn assume(b: bool); //~ ERROR intrinsic safety mismatch + //~^ ERROR intrinsic safety mismatch } fn main() {} diff --git a/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr b/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr index 0c2f3be491dd..b6961275e188 100644 --- a/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr +++ b/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr @@ -5,10 +5,26 @@ LL | fn size_of() -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `assume` - --> $DIR/safe-intrinsic-mismatch.rs:8:5 + --> $DIR/safe-intrinsic-mismatch.rs:9:5 | LL | fn assume(b: bool); | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `size_of` + --> $DIR/safe-intrinsic-mismatch.rs:5:5 + | +LL | fn size_of() -> usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `assume` + --> $DIR/safe-intrinsic-mismatch.rs:9:5 + | +LL | fn assume(b: bool); + | ^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors diff --git a/tests/ui/issues/issue-31910.rs b/tests/ui/issues/issue-31910.rs index e0655d3f6dbf..19cfc4627c75 100644 --- a/tests/ui/issues/issue-31910.rs +++ b/tests/ui/issues/issue-31910.rs @@ -1,4 +1,5 @@ enum Enum { + //~^ ERROR: `T` is never used X = Trait::Number, //~^ ERROR mismatched types //~| expected `isize`, found `i32` diff --git a/tests/ui/issues/issue-31910.stderr b/tests/ui/issues/issue-31910.stderr index 6ef84d7daef5..89a6d5574a1e 100644 --- a/tests/ui/issues/issue-31910.stderr +++ b/tests/ui/issues/issue-31910.stderr @@ -1,9 +1,18 @@ error[E0308]: mismatched types - --> $DIR/issue-31910.rs:2:9 + --> $DIR/issue-31910.rs:3:9 | LL | X = Trait::Number, | ^^^^^^^^^^^^^ expected `isize`, found `i32` -error: aborting due to 1 previous error +error[E0392]: parameter `T` is never used + --> $DIR/issue-31910.rs:1:11 + | +LL | enum Enum { + | ^ unused parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` -For more information about this error, try `rustc --explain E0308`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0392. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-3214.rs b/tests/ui/issues/issue-3214.rs index b2c27f5be957..03d8d6ba2465 100644 --- a/tests/ui/issues/issue-3214.rs +++ b/tests/ui/issues/issue-3214.rs @@ -5,6 +5,7 @@ fn foo() { impl Drop for Foo { //~^ ERROR struct takes 0 generic arguments but 1 generic argument + //~| ERROR `T` is not constrained fn drop(&mut self) {} } } diff --git a/tests/ui/issues/issue-3214.stderr b/tests/ui/issues/issue-3214.stderr index 5b57c1baf90b..26ac6d39f601 100644 --- a/tests/ui/issues/issue-3214.stderr +++ b/tests/ui/issues/issue-3214.stderr @@ -22,7 +22,13 @@ note: struct defined here, with 0 generic parameters LL | struct Foo { | ^^^ -error: aborting due to 2 previous errors +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-3214.rs:6:10 + | +LL | impl Drop for Foo { + | ^ unconstrained type parameter -Some errors have detailed explanations: E0107, E0401. +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0207, E0401. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/issues/issue-34373.rs b/tests/ui/issues/issue-34373.rs index ca24e37d9bb1..dc20c5589b33 100644 --- a/tests/ui/issues/issue-34373.rs +++ b/tests/ui/issues/issue-34373.rs @@ -5,6 +5,8 @@ trait Trait { } pub struct Foo>>; //~ ERROR cycle detected +//~^ ERROR `T` is never used +//~| ERROR `Trait` cannot be made into an object type DefaultFoo = Foo; fn main() { diff --git a/tests/ui/issues/issue-34373.stderr b/tests/ui/issues/issue-34373.stderr index c6906734b2de..1a1cfc925b77 100644 --- a/tests/ui/issues/issue-34373.stderr +++ b/tests/ui/issues/issue-34373.stderr @@ -5,7 +5,7 @@ LL | pub struct Foo>>; | ^^^^^^^^^^ | note: ...which requires expanding type alias `DefaultFoo`... - --> $DIR/issue-34373.rs:8:19 + --> $DIR/issue-34373.rs:10:19 | LL | type DefaultFoo = Foo; | ^^^ @@ -23,6 +23,38 @@ LL | | } | |_^ = 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: aborting due to 1 previous error +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/issue-34373.rs:7:24 + | +LL | pub struct Foo>>; + | ^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/issue-34373.rs:4:8 + | +LL | trait Trait { + | ----- this trait cannot be made into an object... +LL | fn foo(_: T) {} + | ^^^ ...because associated function `foo` has no `self` parameter +help: consider turning `foo` into a method by giving it a `&self` argument + | +LL | fn foo(&self, _: T) {} + | ++++++ +help: alternatively, consider constraining `foo` so it does not apply to trait objects + | +LL | fn foo(_: T) where Self: Sized {} + | +++++++++++++++++ -For more information about this error, try `rustc --explain E0391`. +error[E0392]: parameter `T` is never used + --> $DIR/issue-34373.rs:7:16 + | +LL | pub struct Foo>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ unused parameter + | + = 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: usize` instead + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0038, E0391, E0392. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/lang-items/lang-item-generic-requirements.rs b/tests/ui/lang-items/lang-item-generic-requirements.rs index 21bd7187e15b..697790023d6c 100644 --- a/tests/ui/lang-items/lang-item-generic-requirements.rs +++ b/tests/ui/lang-items/lang-item-generic-requirements.rs @@ -22,8 +22,8 @@ trait MyIndex<'a, T> {} #[lang = "phantom_data"] //~^ ERROR `phantom_data` language item must be applied to a struct with 1 generic argument struct MyPhantomData; -//~^ ERROR parameter `T` is never used -//~| ERROR parameter `U` is never used +//~^ ERROR `T` is never used +//~| ERROR `U` is never used #[lang = "owned_box"] //~^ ERROR `owned_box` language item must be applied to a struct with at least 1 generic argument @@ -41,8 +41,7 @@ fn ice() { // Use add let r = 5; let a = 6; - r + a; - //~^ ERROR cannot add `{integer}` to `{integer}` + r + a; //~ ERROR cannot add // Use drop in place my_ptr_drop(); diff --git a/tests/ui/lifetimes/issue-95023.rs b/tests/ui/lifetimes/issue-95023.rs index 3fba8c00c572..e35f1a36e2a2 100644 --- a/tests/ui/lifetimes/issue-95023.rs +++ b/tests/ui/lifetimes/issue-95023.rs @@ -3,7 +3,9 @@ struct Error(ErrorKind); impl Fn(&isize) for Error { //~^ ERROR manual implementations of `Fn` are experimental [E0183] //~^^ ERROR associated type bindings are not allowed here [E0229] - fn foo(&self) -> Self::B<{N}>; + //~| ERROR not all trait items implemented + //~| ERROR expected a `FnMut(&isize)` closure, found `Error` + fn foo(&self) -> Self::B<{ N }>; //~^ ERROR associated function in `impl` without body //~^^ ERROR method `foo` is not a member of trait `Fn` [E0407] //~^^^ ERROR associated type `B` not found for `Self` [E0220] diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr index 6361d8ad30bd..b9c95d3e49a4 100644 --- a/tests/ui/lifetimes/issue-95023.stderr +++ b/tests/ui/lifetimes/issue-95023.stderr @@ -1,16 +1,16 @@ error: associated function in `impl` without body - --> $DIR/issue-95023.rs:6:5 + --> $DIR/issue-95023.rs:8:5 | -LL | fn foo(&self) -> Self::B<{N}>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: provide a definition for the function: `{ }` +LL | fn foo(&self) -> Self::B<{ N }>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the function: `{ }` error[E0407]: method `foo` is not a member of trait `Fn` - --> $DIR/issue-95023.rs:6:5 + --> $DIR/issue-95023.rs:8:5 | -LL | fn foo(&self) -> Self::B<{N}>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `Fn` +LL | fn foo(&self) -> Self::B<{ N }>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `Fn` error[E0183]: manual implementations of `Fn` are experimental --> $DIR/issue-95023.rs:3:6 @@ -33,12 +33,30 @@ LL | impl Fn(&isize) for Error { | ^^^^^^^^^^ error[E0220]: associated type `B` not found for `Self` - --> $DIR/issue-95023.rs:6:44 + --> $DIR/issue-95023.rs:8:44 | -LL | fn foo(&self) -> Self::B<{N}>; +LL | fn foo(&self) -> Self::B<{ N }>; | ^ help: `Self` has the following associated type: `Output` -error: aborting due to 5 previous errors +error[E0277]: expected a `FnMut(&isize)` closure, found `Error` + --> $DIR/issue-95023.rs:3:21 + | +LL | impl Fn(&isize) for Error { + | ^^^^^ expected an `FnMut(&isize)` closure, found `Error` + | + = help: the trait `FnMut<(&isize,)>` is not implemented for `Error` +note: required by a bound in `Fn` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL -Some errors have detailed explanations: E0183, E0220, E0229, E0407. -For more information about an error, try `rustc --explain E0183`. +error[E0046]: not all trait items implemented, missing: `call` + --> $DIR/issue-95023.rs:3:1 + | +LL | impl Fn(&isize) for Error { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation + | + = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0046, E0183, E0220, E0229, E0277, E0407. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-alias.rs b/tests/ui/lifetimes/missing-lifetime-in-alias.rs index 51c564c011a8..a0887b36b8a9 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-alias.rs +++ b/tests/ui/lifetimes/missing-lifetime-in-alias.rs @@ -4,6 +4,7 @@ trait Trait<'a> { type Bar<'b> //~^ NOTE associated type defined here, with 1 lifetime parameter //~| NOTE + //~| NOTE where Self: 'b; } @@ -13,6 +14,8 @@ struct Impl<'a>(&'a ()); impl<'a> Trait<'a> for Impl<'a> { type Foo = &'a (); type Bar<'b> = &'b (); + //~^ ERROR: does not fulfill the required lifetime + //~| NOTE: type must outlive the lifetime `'b` } type A<'a> = Impl<'a>; diff --git a/tests/ui/lifetimes/missing-lifetime-in-alias.stderr b/tests/ui/lifetimes/missing-lifetime-in-alias.stderr index 20159e144072..9183e6302eef 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-alias.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-alias.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-in-alias.rs:20:24 + --> $DIR/missing-lifetime-in-alias.rs:23:24 | LL | type B<'a> = as Trait>::Foo; | ^^^^^ expected named lifetime parameter @@ -10,13 +10,13 @@ LL | type B<'a> = as Trait<'a>>::Foo; | ++++ error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-in-alias.rs:24:28 + --> $DIR/missing-lifetime-in-alias.rs:27:28 | LL | type C<'a, 'b> = as Trait>::Bar; | ^^^^^ expected named lifetime parameter | note: these named lifetimes are available to use - --> $DIR/missing-lifetime-in-alias.rs:24:8 + --> $DIR/missing-lifetime-in-alias.rs:27:8 | LL | type C<'a, 'b> = as Trait>::Bar; | ^^ ^^ @@ -26,7 +26,7 @@ LL | type C<'a, 'b> = as Trait<'lifetime>>::Bar; | +++++++++++ error[E0107]: missing generics for associated type `Trait::Bar` - --> $DIR/missing-lifetime-in-alias.rs:24:36 + --> $DIR/missing-lifetime-in-alias.rs:27:36 | LL | type C<'a, 'b> = as Trait>::Bar; | ^^^ expected 1 lifetime argument @@ -41,7 +41,26 @@ help: add missing lifetime argument LL | type C<'a, 'b> = as Trait>::Bar<'a>; | ++++ -error: aborting due to 3 previous errors +error[E0477]: the type `Impl<'a>` does not fulfill the required lifetime + --> $DIR/missing-lifetime-in-alias.rs:16:20 + | +LL | type Bar<'b> + | ------------ definition of `Bar` from trait +... +LL | type Bar<'b> = &'b (); + | ^^^^^^ + | +note: type must outlive the lifetime `'b` as defined here + --> $DIR/missing-lifetime-in-alias.rs:16:14 + | +LL | type Bar<'b> = &'b (); + | ^^ +help: copy the `where` clause predicates from the trait + | +LL | type Bar<'b> = &'b () where Self: 'b; + | ++++++++++++++ -Some errors have detailed explanations: E0106, E0107. +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0106, E0107, E0477. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs index 51be999a6329..345c8a25f79f 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs @@ -15,6 +15,7 @@ fn is_static(_: T) where T: 'static { } // code forces us into a conservative, hacky path. fn bar(x: &str) -> &dyn Foo { &() } //~^ ERROR please supply an explicit bound +//~| ERROR `(): Foo<'_>` is not satisfied fn main() { let s = format!("foo"); diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr index 688f8af0822b..d227c8778fe5 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr @@ -4,6 +4,20 @@ error[E0228]: the lifetime bound for this object type cannot be deduced from con LL | fn bar(x: &str) -> &dyn Foo { &() } | ^^^^^^^ -error: aborting due to 1 previous error +error[E0277]: the trait bound `(): Foo<'_>` is not satisfied + --> $DIR/object-lifetime-default-dyn-binding-nonstatic3.rs:16:47 + | +LL | fn bar(x: &str) -> &dyn Foo { &() } + | ^^^ the trait `Foo<'_>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/object-lifetime-default-dyn-binding-nonstatic3.rs:4:1 + | +LL | trait Foo<'a> { + | ^^^^^^^^^^^^^ + = note: required for the cast from `&()` to `&dyn Foo<'_, Item = dyn Bar>` -For more information about this error, try `rustc --explain E0228`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0228, E0277. +For more information about an error, try `rustc --explain E0228`. diff --git a/tests/ui/object-safety/object-safety-supertrait-mentions-Self.rs b/tests/ui/object-safety/object-safety-supertrait-mentions-Self.rs index 2445b33c8144..d96c7ba72a3c 100644 --- a/tests/ui/object-safety/object-safety-supertrait-mentions-Self.rs +++ b/tests/ui/object-safety/object-safety-supertrait-mentions-Self.rs @@ -6,6 +6,7 @@ trait Bar { } trait Baz : Bar { + //~^ ERROR the size for values of type `Self` cannot be known } fn make_bar>(t: &T) -> &dyn Bar { diff --git a/tests/ui/object-safety/object-safety-supertrait-mentions-Self.stderr b/tests/ui/object-safety/object-safety-supertrait-mentions-Self.stderr index fc476691d014..22adc19c8029 100644 --- a/tests/ui/object-safety/object-safety-supertrait-mentions-Self.stderr +++ b/tests/ui/object-safety/object-safety-supertrait-mentions-Self.stderr @@ -1,5 +1,5 @@ error[E0038]: the trait `Baz` cannot be made into an object - --> $DIR/object-safety-supertrait-mentions-Self.rs:15:31 + --> $DIR/object-safety-supertrait-mentions-Self.rs:16:31 | LL | fn make_baz(t: &T) -> &dyn Baz { | ^^^^^^^ `Baz` cannot be made into an object @@ -16,6 +16,27 @@ help: consider using an opaque type instead LL | fn make_baz(t: &T) -> &impl Baz { | ~~~~ -error: aborting due to 1 previous error +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/object-safety-supertrait-mentions-Self.rs:8:13 + | +LL | trait Baz : Bar { + | ^^^^^^^^^ doesn't have a size known at compile-time + | +note: required by a bound in `Bar` + --> $DIR/object-safety-supertrait-mentions-Self.rs:4:11 + | +LL | trait Bar { + | ^ required by this bound in `Bar` +help: consider further restricting `Self` + | +LL | trait Baz : Bar + Sized { + | +++++++ +help: consider relaxing the implicit `Sized` restriction + | +LL | trait Bar { + | ++++++++ -For more information about this error, try `rustc --explain E0038`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0038, E0277. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/parser/impl-item-type-no-body-semantic-fail.rs b/tests/ui/parser/impl-item-type-no-body-semantic-fail.rs index 1291a021bef5..5582e82d11d0 100644 --- a/tests/ui/parser/impl-item-type-no-body-semantic-fail.rs +++ b/tests/ui/parser/impl-item-type-no-body-semantic-fail.rs @@ -17,4 +17,5 @@ impl X { type W where Self: Eq; //~^ ERROR associated type in `impl` without body //~| ERROR inherent associated types are unstable + //~| ERROR duplicate definitions } diff --git a/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr b/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr index 3856754e080a..5bcbbb9deb75 100644 --- a/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr +++ b/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr @@ -78,6 +78,16 @@ LL | type W where Self: Eq; = note: see issue #8995 for more information = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable -error: aborting due to 10 previous errors +error[E0592]: duplicate definitions with name `W` + --> $DIR/impl-item-type-no-body-semantic-fail.rs:17:5 + | +LL | type W: Ord where Self: Eq; + | ------ other definition for `W` +... +LL | type W where Self: Eq; + | ^^^^^^ duplicate definitions for `W` -For more information about this error, try `rustc --explain E0658`. +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0592, E0658. +For more information about an error, try `rustc --explain E0592`. diff --git a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs index 5f731f8db775..cf754a6854ec 100644 --- a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs +++ b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs @@ -16,4 +16,5 @@ fn y<'a>(y: &mut 'a + Send) { //~| ERROR at least one trait is required for an object type let z = y as &mut 'a + Send; //~^ ERROR expected value, found trait `Send` + //~| ERROR at least one trait is required for an object type } diff --git a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr index 799bc16bd6ab..652aeff5dd44 100644 --- a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr +++ b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr @@ -33,7 +33,13 @@ error[E0224]: at least one trait is required for an object type LL | fn y<'a>(y: &mut 'a + Send) { | ^^ -error: aborting due to 5 previous errors +error[E0224]: at least one trait is required for an object type + --> $DIR/issue-73568-lifetime-after-mut.rs:17:23 + | +LL | let z = y as &mut 'a + Send; + | ^^ + +error: aborting due to 6 previous errors Some errors have detailed explanations: E0178, E0224, E0423. For more information about an error, try `rustc --explain E0178`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr index f0b6e2b1c255..d20404e63b3f 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr @@ -10,5 +10,15 @@ LL | impl const A for () {} = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` = note: adding a non-const method body in the future would be a breaking change -error: aborting due to 1 previous error +error[E0207]: the const parameter `host` is not constrained by the impl trait, self type, or predicates + --> $DIR/const-impl-requires-const-trait.rs:8:6 + | +LL | impl const A for () {} + | ^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr index dfe8fa79e264..4b009446dbc0 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr @@ -8,5 +8,11 @@ LL | #[derive_const(Default)] = note: adding a non-const method body in the future would be a breaking change = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error +error[E0207]: the const parameter `host` is not constrained by the impl trait, self type, or predicates + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr index c561f80653c9..8f374bc4d8f2 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr @@ -23,6 +23,21 @@ LL | #[derive_const(Default, PartialEq)] = note: adding a non-const method body in the future would be a breaking change = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error[E0207]: the const parameter `host` is not constrained by the impl trait, self type, or predicates + --> $DIR/derive-const-use.rs:7:6 + | +LL | impl const Default for A { + | ^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported -For more information about this error, try `rustc --explain E0635`. +error[E0207]: the const parameter `host` is not constrained by the impl trait, self type, or predicates + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0207, E0635. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs index c6be75a6a2f0..7d817d09c7f0 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs @@ -1,12 +1,14 @@ #![feature(const_trait_impl, effects)] const fn test() -> impl ~const Fn() { //~ ERROR `~const` can only be applied to `#[const_trait]` traits + //~^ ERROR cycle detected const move || { //~ ERROR const closures are experimental let sl: &[u8] = b"foo"; match sl { [first, remainder @ ..] => { assert_eq!(first, &b'f'); + //~^ ERROR can't compare `&u8` with `&u8` } [] => panic!(), } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr index fe6b613d1549..7e268f50dca3 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr @@ -1,5 +1,5 @@ error[E0658]: const closures are experimental - --> $DIR/ice-112822-expected-type-for-param.rs:4:5 + --> $DIR/ice-112822-expected-type-for-param.rs:5:5 | LL | const move || { | ^^^^^ @@ -13,6 +13,47 @@ error: `~const` can only be applied to `#[const_trait]` traits LL | const fn test() -> impl ~const Fn() { | ^^^^ -error: aborting due to 2 previous errors +error[E0277]: can't compare `&u8` with `&u8` + --> $DIR/ice-112822-expected-type-for-param.rs:10:17 + | +LL | assert_eq!(first, &b'f'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `&u8 == &u8` + | + = help: the trait `~const PartialEq<&u8>` is not implemented for `&u8` + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) -For more information about this error, try `rustc --explain E0658`. +error[E0391]: cycle detected when computing type of opaque `test::{opaque#0}` + --> $DIR/ice-112822-expected-type-for-param.rs:3:20 + | +LL | const fn test() -> impl ~const Fn() { + | ^^^^^^^^^^^^^^^^ + | +note: ...which requires borrow-checking `test`... + --> $DIR/ice-112822-expected-type-for-param.rs:3:1 + | +LL | const fn test() -> impl ~const Fn() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `test`... + --> $DIR/ice-112822-expected-type-for-param.rs:3:1 + | +LL | const fn test() -> impl ~const Fn() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const checking `test`... + --> $DIR/ice-112822-expected-type-for-param.rs:3:1 + | +LL | const fn test() -> impl ~const Fn() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires computing whether `test::{opaque#0}` is freeze... + = note: ...which requires evaluating trait selection obligation `test::{opaque#0}: core::marker::Freeze`... + = note: ...which again requires computing type of opaque `test::{opaque#0}`, completing the cycle +note: cycle used when computing type of `test::{opaque#0}` + --> $DIR/ice-112822-expected-type-for-param.rs:3:20 + | +LL | const fn test() -> impl ~const Fn() { + | ^^^^^^^^^^^^^^^^ + = 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: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0391, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr index eae313ef0874..ac25812c42df 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr @@ -24,5 +24,13 @@ LL | trait Bar: ~const Foo {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 3 previous errors +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/super-traits-fail-2.rs:10:19 + | +LL | trait Bar: ~const Foo {} + | ^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr index be3153d6a081..5273f7e48eb8 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr @@ -12,5 +12,13 @@ LL | trait Bar: ~const Foo {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/super-traits-fail-2.rs:10:19 + | +LL | trait Bar: ~const Foo {} + | ^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs index abdf0feee038..120399f0c787 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs @@ -10,7 +10,8 @@ trait Foo { trait Bar: ~const Foo {} //[ny,nn]~^ ERROR: `~const` can only be applied to `#[const_trait]` //[ny,nn]~| ERROR: `~const` can only be applied to `#[const_trait]` -//[yn,nn]~^^^ ERROR: `~const` is not allowed here +//[ny,nn]~| ERROR: `~const` can only be applied to `#[const_trait]` +//[yn,nn]~^^^^ ERROR: `~const` is not allowed here const fn foo(x: &T) { x.a(); diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr index c05c4d50a33d..53445d255903 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr @@ -11,7 +11,7 @@ LL | trait Bar: ~const Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/super-traits-fail-2.rs:16:5 + --> $DIR/super-traits-fail-2.rs:17:5 | LL | x.a(); | ^^^^^ expected `host`, found `true` diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr index 852c02cad5ca..681647945d0a 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/super-traits-fail-2.rs:16:5 + --> $DIR/super-traits-fail-2.rs:17:5 | LL | x.a(); | ^^^^^ expected `host`, found `true` diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr index 834d6f4dcf38..2c63c0217aba 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr @@ -25,10 +25,18 @@ LL | trait Bar: ~const Foo {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/super-traits-fail-3.rs:17:24 + --> $DIR/super-traits-fail-3.rs:18:24 | LL | const fn foo(x: &T) { | ^^^ -error: aborting due to 4 previous errors +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/super-traits-fail-3.rs:12:19 + | +LL | trait Bar: ~const Foo {} + | ^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr index 4fdd2284c475..f737cb243df4 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr @@ -12,5 +12,13 @@ LL | trait Bar: ~const Foo {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/super-traits-fail-3.rs:12:19 + | +LL | trait Bar: ~const Foo {} + | ^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs index 30131d5849c8..745668c4dd43 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs @@ -12,11 +12,13 @@ trait Foo { trait Bar: ~const Foo {} //[ny,nn]~^ ERROR: `~const` can only be applied to `#[const_trait]` //[ny,nn]~| ERROR: `~const` can only be applied to `#[const_trait]` -//[yn,nn]~^^^ ERROR: `~const` is not allowed here +//[ny,nn]~| ERROR: `~const` can only be applied to `#[const_trait]` +//[yn,nn]~^^^^ ERROR: `~const` is not allowed here const fn foo(x: &T) { //[yn,nn]~^ ERROR: `~const` can only be applied to `#[const_trait]` x.a(); + //[yn]~^ ERROR: mismatched types } fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr index ab7c814eb498..de7a11cf1551 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr @@ -11,10 +11,20 @@ LL | trait Bar: ~const Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/super-traits-fail-3.rs:17:24 + --> $DIR/super-traits-fail-3.rs:18:24 | LL | const fn foo(x: &T) { | ^^^ -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/super-traits-fail-3.rs:20:5 + | +LL | x.a(); + | ^^^^^ expected `host`, found `true` + | + = note: expected constant `host` + found constant `true` +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs index 5ecb75094f03..9d220686771e 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs @@ -9,10 +9,12 @@ fn non_const_function() {} //~ ERROR `~const` is not allowed struct Struct { field: T } //~ ERROR `~const` is not allowed here struct TupleStruct(T); //~ ERROR `~const` is not allowed here struct UnitStruct; //~ ERROR `~const` is not allowed here +//~^ ERROR parameter `T` is never used enum Enum { Variant(T) } //~ ERROR `~const` is not allowed here union Union { field: T } //~ ERROR `~const` is not allowed here +//~^ ERROR field must implement `Copy` type Type = T; //~ ERROR `~const` is not allowed here @@ -30,6 +32,7 @@ trait NonConstTrait { impl NonConstTrait for () { type Type = (); //~ ERROR `~const` is not allowed + //~^ ERROR overflow evaluating the requirement `(): Trait` fn non_const_function() {} //~ ERROR `~const` is not allowed const CONSTANT: () = (); //~ ERROR `~const` is not allowed //~^ ERROR generic const items are experimental diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr index 497ec5bcf84d..a54ba7a94b4a 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr @@ -35,7 +35,7 @@ LL | struct UnitStruct; = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:13:14 + --> $DIR/tilde-const-invalid-places.rs:14:14 | LL | enum Enum { Variant(T) } | ^^^^^^ @@ -43,7 +43,7 @@ LL | enum Enum { Variant(T) } = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:15:16 + --> $DIR/tilde-const-invalid-places.rs:16:16 | LL | union Union { field: T } | ^^^^^^ @@ -51,7 +51,7 @@ LL | union Union { field: T } = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:17:14 + --> $DIR/tilde-const-invalid-places.rs:19:14 | LL | type Type = T; | ^^^^^^ @@ -59,7 +59,7 @@ LL | type Type = T; = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:19:19 + --> $DIR/tilde-const-invalid-places.rs:21:19 | LL | const CONSTANT: () = (); | ^^^^^^ @@ -67,7 +67,7 @@ LL | const CONSTANT: () = (); = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:23:18 + --> $DIR/tilde-const-invalid-places.rs:25:18 | LL | type Type: ~const Trait; | ^^^^^^ @@ -75,7 +75,7 @@ LL | type Type: ~const Trait; = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:23:33 + --> $DIR/tilde-const-invalid-places.rs:25:33 | LL | type Type: ~const Trait; | ^^^^^^ @@ -83,19 +83,19 @@ LL | type Type: ~const Trait; = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:26:30 + --> $DIR/tilde-const-invalid-places.rs:28:30 | LL | fn non_const_function(); | ^^^^^^ | note: this function is not `const`, so it cannot have `~const` trait bounds - --> $DIR/tilde-const-invalid-places.rs:26:8 + --> $DIR/tilde-const-invalid-places.rs:28:8 | LL | fn non_const_function(); | ^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:27:23 + --> $DIR/tilde-const-invalid-places.rs:29:23 | LL | const CONSTANT: (); | ^^^^^^ @@ -103,7 +103,7 @@ LL | const CONSTANT: (); = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:32:18 + --> $DIR/tilde-const-invalid-places.rs:34:18 | LL | type Type = (); | ^^^^^^ @@ -111,19 +111,19 @@ LL | type Type = (); = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:33:30 + --> $DIR/tilde-const-invalid-places.rs:36:30 | LL | fn non_const_function() {} | ^^^^^^ | note: this function is not `const`, so it cannot have `~const` trait bounds - --> $DIR/tilde-const-invalid-places.rs:33:8 + --> $DIR/tilde-const-invalid-places.rs:36:8 | LL | fn non_const_function() {} | ^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:34:23 + --> $DIR/tilde-const-invalid-places.rs:37:23 | LL | const CONSTANT: () = (); | ^^^^^^ @@ -131,7 +131,7 @@ LL | const CONSTANT: () = (); = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:41:18 + --> $DIR/tilde-const-invalid-places.rs:44:18 | LL | type Type = (); | ^^^^^^ @@ -139,19 +139,19 @@ LL | type Type = (); = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:43:30 + --> $DIR/tilde-const-invalid-places.rs:46:30 | LL | fn non_const_function() {} | ^^^^^^ | note: this function is not `const`, so it cannot have `~const` trait bounds - --> $DIR/tilde-const-invalid-places.rs:43:8 + --> $DIR/tilde-const-invalid-places.rs:46:8 | LL | fn non_const_function() {} | ^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:44:23 + --> $DIR/tilde-const-invalid-places.rs:47:23 | LL | const CONSTANT: () = (); | ^^^^^^ @@ -159,55 +159,55 @@ LL | const CONSTANT: () = (); = note: this item cannot have `~const` trait bounds error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:49:15 + --> $DIR/tilde-const-invalid-places.rs:52:15 | LL | trait Child0: ~const Trait {} | ^^^^^^ | note: this trait is not a `#[const_trait]`, so it cannot have `~const` trait bounds - --> $DIR/tilde-const-invalid-places.rs:49:1 + --> $DIR/tilde-const-invalid-places.rs:52:1 | LL | trait Child0: ~const Trait {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:50:26 + --> $DIR/tilde-const-invalid-places.rs:53:26 | LL | trait Child1 where Self: ~const Trait {} | ^^^^^^ | note: this trait is not a `#[const_trait]`, so it cannot have `~const` trait bounds - --> $DIR/tilde-const-invalid-places.rs:50:1 + --> $DIR/tilde-const-invalid-places.rs:53:1 | LL | trait Child1 where Self: ~const Trait {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:53:9 + --> $DIR/tilde-const-invalid-places.rs:56:9 | LL | impl Trait for T {} | ^^^^^^ | note: this impl is not `const`, so it cannot have `~const` trait bounds - --> $DIR/tilde-const-invalid-places.rs:53:1 + --> $DIR/tilde-const-invalid-places.rs:56:1 | LL | impl Trait for T {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `~const` is not allowed here - --> $DIR/tilde-const-invalid-places.rs:56:9 + --> $DIR/tilde-const-invalid-places.rs:59:9 | LL | impl Struct {} | ^^^^^^ | note: inherent impls cannot have `~const` trait bounds - --> $DIR/tilde-const-invalid-places.rs:56:1 + --> $DIR/tilde-const-invalid-places.rs:59:1 | LL | impl Struct {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0658]: generic const items are experimental - --> $DIR/tilde-const-invalid-places.rs:19:15 + --> $DIR/tilde-const-invalid-places.rs:21:15 | LL | const CONSTANT: () = (); | ^^^^^^^^^^^^^^^^^ @@ -216,7 +216,7 @@ LL | const CONSTANT: () = (); = help: add `#![feature(generic_const_items)]` to the crate attributes to enable error[E0658]: generic const items are experimental - --> $DIR/tilde-const-invalid-places.rs:27:19 + --> $DIR/tilde-const-invalid-places.rs:29:19 | LL | const CONSTANT: (); | ^^^^^^^^^^^^^^^^^ @@ -225,7 +225,7 @@ LL | const CONSTANT: (); = help: add `#![feature(generic_const_items)]` to the crate attributes to enable error[E0658]: generic const items are experimental - --> $DIR/tilde-const-invalid-places.rs:34:19 + --> $DIR/tilde-const-invalid-places.rs:37:19 | LL | const CONSTANT: () = (); | ^^^^^^^^^^^^^^^^^ @@ -234,7 +234,7 @@ LL | const CONSTANT: () = (); = help: add `#![feature(generic_const_items)]` to the crate attributes to enable error[E0658]: generic const items are experimental - --> $DIR/tilde-const-invalid-places.rs:44:19 + --> $DIR/tilde-const-invalid-places.rs:47:19 | LL | const CONSTANT: () = (); | ^^^^^^^^^^^^^^^^^ @@ -243,7 +243,7 @@ LL | const CONSTANT: () = (); = help: add `#![feature(generic_const_items)]` to the crate attributes to enable error[E0658]: inherent associated types are unstable - --> $DIR/tilde-const-invalid-places.rs:41:5 + --> $DIR/tilde-const-invalid-places.rs:44:5 | LL | type Type = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -251,6 +251,39 @@ LL | type Type = (); = note: see issue #8995 for more information = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable -error: aborting due to 27 previous errors +error[E0392]: parameter `T` is never used + --> $DIR/tilde-const-invalid-places.rs:11:19 + | +LL | struct UnitStruct; + | ^ unused parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` -For more information about this error, try `rustc --explain E0658`. +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union + --> $DIR/tilde-const-invalid-places.rs:16:32 + | +LL | union Union { field: T } + | ^^^^^^^^ + | + = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | union Union { field: std::mem::ManuallyDrop } + | +++++++++++++++++++++++ + + +error[E0275]: overflow evaluating the requirement `(): Trait` + --> $DIR/tilde-const-invalid-places.rs:34:34 + | +LL | type Type = (); + | ^^ + | +note: required by a bound in `NonConstTrait::Type` + --> $DIR/tilde-const-invalid-places.rs:25:33 + | +LL | type Type: ~const Trait; + | ^^^^^^^^^^^^ required by this bound in `NonConstTrait::Type` + +error: aborting due to 30 previous errors + +Some errors have detailed explanations: E0275, E0392, E0658, E0740. +For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/stability-attribute/generics-default-stability-where.rs b/tests/ui/stability-attribute/generics-default-stability-where.rs index 4afbca262649..142de12e1529 100644 --- a/tests/ui/stability-attribute/generics-default-stability-where.rs +++ b/tests/ui/stability-attribute/generics-default-stability-where.rs @@ -5,6 +5,7 @@ extern crate unstable_generic_param; use unstable_generic_param::*; impl Trait3 for T where T: Trait2 { //~ ERROR use of unstable library feature 'unstable_default' +//~^ ERROR `T` must be used as the type parameter for some local type fn foo() -> usize { T::foo() } } diff --git a/tests/ui/stability-attribute/generics-default-stability-where.stderr b/tests/ui/stability-attribute/generics-default-stability-where.stderr index ce34f96771c4..16b560e8a4be 100644 --- a/tests/ui/stability-attribute/generics-default-stability-where.stderr +++ b/tests/ui/stability-attribute/generics-default-stability-where.stderr @@ -6,6 +6,16 @@ LL | impl Trait3 for T where T: Trait2 { | = help: add `#![feature(unstable_default)]` to the crate attributes to enable -error: aborting due to 1 previous error +error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct`) + --> $DIR/generics-default-stability-where.rs:7:6 + | +LL | impl Trait3 for T where T: Trait2 { + | ^ type parameter `T` must be used as the type parameter for some local type + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local + = note: only traits defined in the current crate can be implemented for a type parameter -For more information about this error, try `rustc --explain E0658`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0210, E0658. +For more information about an error, try `rustc --explain E0210`. diff --git a/tests/ui/suggestions/bad-infer-in-trait-impl.rs b/tests/ui/suggestions/bad-infer-in-trait-impl.rs index 87db2636fb24..f38b168037b6 100644 --- a/tests/ui/suggestions/bad-infer-in-trait-impl.rs +++ b/tests/ui/suggestions/bad-infer-in-trait-impl.rs @@ -5,6 +5,7 @@ trait Foo { impl Foo for () { fn bar(s: _) {} //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~| ERROR has 1 parameter but the declaration in trait `Foo::bar` has 0 } fn main() {} diff --git a/tests/ui/suggestions/bad-infer-in-trait-impl.stderr b/tests/ui/suggestions/bad-infer-in-trait-impl.stderr index d96ee33a9146..50c398de2b08 100644 --- a/tests/ui/suggestions/bad-infer-in-trait-impl.stderr +++ b/tests/ui/suggestions/bad-infer-in-trait-impl.stderr @@ -9,6 +9,16 @@ help: use type parameters instead LL | fn bar(s: T) {} | +++ ~ -error: aborting due to 1 previous error +error[E0050]: method `bar` has 1 parameter but the declaration in trait `Foo::bar` has 0 + --> $DIR/bad-infer-in-trait-impl.rs:6:15 + | +LL | fn bar(); + | --------- trait requires 0 parameters +... +LL | fn bar(s: _) {} + | ^ expected 0 parameters, found 1 -For more information about this error, try `rustc --explain E0121`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0050, E0121. +For more information about an error, try `rustc --explain E0050`. diff --git a/tests/ui/suggestions/fn-trait-notation.fixed b/tests/ui/suggestions/fn-trait-notation.fixed index cf940f4e9267..6cb97df4a859 100644 --- a/tests/ui/suggestions/fn-trait-notation.fixed +++ b/tests/ui/suggestions/fn-trait-notation.fixed @@ -2,6 +2,7 @@ fn e0658(f: F, g: G, h: H) -> i32 where F: Fn(i32) -> i32, //~ ERROR E0658 + //~^ ERROR E0059 G: Fn(i32, i32) -> (i32, i32), //~ ERROR E0658 H: Fn(i32) -> i32, //~ ERROR E0658 { diff --git a/tests/ui/suggestions/fn-trait-notation.rs b/tests/ui/suggestions/fn-trait-notation.rs index f0bb03315d98..912516149301 100644 --- a/tests/ui/suggestions/fn-trait-notation.rs +++ b/tests/ui/suggestions/fn-trait-notation.rs @@ -2,6 +2,7 @@ fn e0658(f: F, g: G, h: H) -> i32 where F: Fn, //~ ERROR E0658 + //~^ ERROR E0059 G: Fn<(i32, i32, ), Output = (i32, i32)>, //~ ERROR E0658 H: Fn<(i32,), Output = i32>, //~ ERROR E0658 { diff --git a/tests/ui/suggestions/fn-trait-notation.stderr b/tests/ui/suggestions/fn-trait-notation.stderr index 3e3b54174401..ed79b3d512cd 100644 --- a/tests/ui/suggestions/fn-trait-notation.stderr +++ b/tests/ui/suggestions/fn-trait-notation.stderr @@ -8,7 +8,7 @@ LL | F: Fn, = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change - --> $DIR/fn-trait-notation.rs:5:8 + --> $DIR/fn-trait-notation.rs:6:8 | LL | G: Fn<(i32, i32, ), Output = (i32, i32)>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use parenthetical notation instead: `Fn(i32, i32) -> (i32, i32)` @@ -17,7 +17,7 @@ LL | G: Fn<(i32, i32, ), Output = (i32, i32)>, = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change - --> $DIR/fn-trait-notation.rs:6:8 + --> $DIR/fn-trait-notation.rs:7:8 | LL | H: Fn<(i32,), Output = i32>, | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use parenthetical notation instead: `Fn(i32) -> i32` @@ -25,6 +25,16 @@ LL | H: Fn<(i32,), Output = i32>, = note: see issue #29625 for more information = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error: aborting due to 3 previous errors +error[E0059]: type parameter to bare `Fn` trait must be a tuple + --> $DIR/fn-trait-notation.rs:4:8 + | +LL | F: Fn, + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `i32` + | +note: required by a bound in `Fn` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL -For more information about this error, try `rustc --explain E0658`. +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0059, E0658. +For more information about an error, try `rustc --explain E0059`. diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs index a1a51c4814e8..afde5ee97d78 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs @@ -18,6 +18,7 @@ mod elided { // But that lifetime does not participate in resolution. async fn i(mut x: impl Iterator) -> Option<&()> { x.next() } //~^ ERROR missing lifetime specifier + //~| ERROR lifetime may not live long enough } mod underscore { @@ -36,6 +37,7 @@ mod underscore { // But that lifetime does not participate in resolution. async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } //~^ ERROR missing lifetime specifier + //~| ERROR lifetime may not live long enough } mod alone_in_path { @@ -61,8 +63,8 @@ mod in_path { } // This must not err, as the `&` actually resolves to `'a`. -fn resolved_anonymous<'a, T>(f: impl Fn(&'a str) -> &T) { - f("f") +fn resolved_anonymous<'a, T: 'a>(f: impl Fn(&'a str) -> &T) { + f("f"); } fn main() {} diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index 2dfaa7311948..fee4c7268fae 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -41,7 +41,7 @@ LL + async fn i(mut x: impl Iterator) -> Option<()> { x.next() } | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime-gated.rs:27:58 + --> $DIR/impl-trait-missing-lifetime-gated.rs:28:58 | LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } | ^^ expected named lifetime parameter @@ -62,7 +62,7 @@ LL + fn g(mut x: impl Iterator) -> Option<()> { x.next() } | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime-gated.rs:37:64 + --> $DIR/impl-trait-missing-lifetime-gated.rs:38:64 | LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } | ^^ expected named lifetime parameter @@ -83,7 +83,7 @@ LL + async fn i(mut x: impl Iterator) -> Option<()> { x.next( | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime-gated.rs:47:37 + --> $DIR/impl-trait-missing-lifetime-gated.rs:49:37 | LL | fn g(mut x: impl Foo) -> Option<&()> { x.next() } | ^ expected named lifetime parameter @@ -104,7 +104,7 @@ LL + fn g(mut x: impl Foo) -> Option<()> { x.next() } | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime-gated.rs:58:41 + --> $DIR/impl-trait-missing-lifetime-gated.rs:60:41 | LL | fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } | ^ expected named lifetime parameter @@ -149,7 +149,7 @@ LL | fn g<'a>(mut x: impl Iterator) -> Option<&()> { x.next() | ++++ ++ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:24:35 + --> $DIR/impl-trait-missing-lifetime-gated.rs:25:35 | LL | fn f(_: impl Iterator) {} | ^^ expected named lifetime parameter @@ -161,7 +161,7 @@ LL | fn f<'a>(_: impl Iterator) {} | ++++ ~~ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:27:39 + --> $DIR/impl-trait-missing-lifetime-gated.rs:28:39 | LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } | ^^ expected named lifetime parameter @@ -173,7 +173,7 @@ LL | fn g<'a>(mut x: impl Iterator) -> Option<&'_ ()> { x.nex | ++++ ~~ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:44:18 + --> $DIR/impl-trait-missing-lifetime-gated.rs:46:18 | LL | fn f(_: impl Foo) {} | ^^^ expected named lifetime parameter @@ -185,7 +185,7 @@ LL | fn f<'a>(_: impl Foo<'a>) {} | ++++ ++++ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:47:22 + --> $DIR/impl-trait-missing-lifetime-gated.rs:49:22 | LL | fn g(mut x: impl Foo) -> Option<&()> { x.next() } | ^^^ expected named lifetime parameter @@ -197,7 +197,7 @@ LL | fn g<'a>(mut x: impl Foo<'a>) -> Option<&()> { x.next() } | ++++ ++++ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:55:22 + --> $DIR/impl-trait-missing-lifetime-gated.rs:57:22 | LL | fn f(_: impl Foo<()>) {} | ^ expected named lifetime parameter @@ -209,7 +209,7 @@ LL | fn f<'a>(_: impl Foo<'a, ()>) {} | ++++ +++ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:58:26 + --> $DIR/impl-trait-missing-lifetime-gated.rs:60:26 | LL | fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } | ^ expected named lifetime parameter @@ -220,7 +220,23 @@ help: consider introducing a named lifetime parameter LL | fn g<'a>(mut x: impl Foo<'a, ()>) -> Option<&()> { x.next() } | ++++ +++ -error: aborting due to 14 previous errors +error: lifetime may not live long enough + --> $DIR/impl-trait-missing-lifetime-gated.rs:19:67 + | +LL | async fn i(mut x: impl Iterator) -> Option<&()> { x.next() } + | ----------------------------------------------------------- ^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | | + | return type `impl Future>` contains a lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/impl-trait-missing-lifetime-gated.rs:38:73 + | +LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } + | ----------------------------------------------------------------- ^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | | + | return type `impl Future>` contains a lifetime `'1` + +error: aborting due to 16 previous errors Some errors have detailed explanations: E0106, E0658. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/suggestions/missing-lifetime-specifier.rs b/tests/ui/suggestions/missing-lifetime-specifier.rs index cb734e8ba857..01dcc94f7476 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.rs +++ b/tests/ui/suggestions/missing-lifetime-specifier.rs @@ -1,6 +1,9 @@ +// different number of duplicated diagnostics on different targets +// compile-flags: -Zdeduplicate-diagnostics=yes + #![allow(bare_trait_objects)] -use std::collections::HashMap; use std::cell::RefCell; +use std::collections::HashMap; pub union Foo<'t, 'k> { i: &'t i64, @@ -38,18 +41,10 @@ thread_local! { thread_local! { static e: RefCell>>>> = RefCell::new(HashMap::new()); //~^ ERROR union takes 2 lifetime arguments but 1 lifetime argument - //~| ERROR union takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR union takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR union takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR union takes 2 lifetime arguments but 1 lifetime argument was supplied } thread_local! { static f: RefCell>>>> = RefCell::new(HashMap::new()); //~^ ERROR trait takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR trait takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR trait takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR trait takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR trait takes 2 lifetime arguments but 1 lifetime argument was supplied //~| ERROR missing lifetime //~| ERROR missing lifetime } diff --git a/tests/ui/suggestions/missing-lifetime-specifier.stderr b/tests/ui/suggestions/missing-lifetime-specifier.stderr index e41f547ce9b2..7a7ef47c35b7 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.stderr +++ b/tests/ui/suggestions/missing-lifetime-specifier.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:18:44 + --> $DIR/missing-lifetime-specifier.rs:21:44 | LL | static a: RefCell>>> = RefCell::new(HashMap::new()); | ^^^ expected 2 lifetime parameters @@ -11,7 +11,7 @@ LL | static a: RefCell>>>> = RefC | ++++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:18:44 + --> $DIR/missing-lifetime-specifier.rs:21:44 | LL | / thread_local! { LL | | static a: RefCell>>> = RefCell::new(HashMap::new()); @@ -24,7 +24,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:23:44 + --> $DIR/missing-lifetime-specifier.rs:26:44 | LL | static b: RefCell>>> = RefCell::new(HashMap::new()); | ^^^^ expected 2 lifetime parameters @@ -38,7 +38,7 @@ LL | static b: RefCell>> | +++++++ ++++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:23:44 + --> $DIR/missing-lifetime-specifier.rs:26:44 | LL | / thread_local! { LL | | static b: RefCell>>> = RefCell::new(HashMap::new()); @@ -53,7 +53,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 4 lifetimes it is borrowed from error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:28:47 + --> $DIR/missing-lifetime-specifier.rs:31:47 | LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected 2 lifetime parameters @@ -65,7 +65,7 @@ LL | static c: RefCell>>>> = | +++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:28:47 + --> $DIR/missing-lifetime-specifier.rs:31:47 | LL | / thread_local! { LL | | static c: RefCell>>>> = RefCell::new(HashMap::new()); @@ -78,7 +78,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:33:44 + --> $DIR/missing-lifetime-specifier.rs:36:44 | LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | ^ ^ expected 2 lifetime parameters @@ -92,7 +92,7 @@ LL | static d: RefCell $DIR/missing-lifetime-specifier.rs:33:44 + --> $DIR/missing-lifetime-specifier.rs:36:44 | LL | / thread_local! { LL | | static d: RefCell>>>> = RefCell::new(HashMap::new()); @@ -107,7 +107,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 4 lifetimes it is borrowed from error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:47:44 + --> $DIR/missing-lifetime-specifier.rs:46:44 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected named lifetime parameter @@ -119,14 +119,13 @@ LL | static f: RefCell>>>> = | +++++++ error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:47:44 + --> $DIR/missing-lifetime-specifier.rs:46:44 | LL | / thread_local! { LL | | static f: RefCell>>>> = RefCell::new(HashMap::new()); | | ^ expected named lifetime parameter LL | | LL | | -... | LL | | LL | | } | |_- @@ -134,7 +133,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from error[E0107]: union takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:39:44 + --> $DIR/missing-lifetime-specifier.rs:42:44 | LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^ ------- supplied 1 lifetime argument @@ -142,7 +141,7 @@ LL | static e: RefCell>>>> = RefCell: | expected 2 lifetime arguments | note: union defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:11:11 + --> $DIR/missing-lifetime-specifier.rs:14:11 | LL | pub union Qux<'t, 'k, I> { | ^^^ -- -- @@ -151,84 +150,8 @@ help: add missing lifetime argument LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++++ -error[E0107]: union takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:39:44 - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: union defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:11:11 - | -LL | pub union Qux<'t, 'k, I> { - | ^^^ -- -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++ - -error[E0107]: union takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:39:44 - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: union defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:11:11 - | -LL | pub union Qux<'t, 'k, I> { - | ^^^ -- -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++ - -error[E0107]: union takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:39:44 - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: union defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:11:11 - | -LL | pub union Qux<'t, 'k, I> { - | ^^^ -- -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++ - -error[E0107]: union takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:39:44 - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: union defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:11:11 - | -LL | pub union Qux<'t, 'k, I> { - | ^^^ -- -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++ - error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:47:45 + --> $DIR/missing-lifetime-specifier.rs:46:45 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^ ------- supplied 1 lifetime argument @@ -236,7 +159,7 @@ LL | static f: RefCell>>>> = RefCell | expected 2 lifetime arguments | note: trait defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:15:7 + --> $DIR/missing-lifetime-specifier.rs:18:7 | LL | trait Tar<'t, 'k, I> {} | ^^^ -- -- @@ -245,83 +168,7 @@ help: add missing lifetime argument LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++++ -error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:47:45 - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: trait defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:15:7 - | -LL | trait Tar<'t, 'k, I> {} - | ^^^ -- -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++ - -error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:47:45 - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: trait defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:15:7 - | -LL | trait Tar<'t, 'k, I> {} - | ^^^ -- -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++ - -error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:47:45 - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: trait defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:15:7 - | -LL | trait Tar<'t, 'k, I> {} - | ^^^ -- -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++ - -error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:47:45 - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: trait defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:15:7 - | -LL | trait Tar<'t, 'k, I> {} - | ^^^ -- -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++ - -error: aborting due to 20 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0106, E0107. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/tag-type-args.rs b/tests/ui/tag-type-args.rs index 660d860ba772..75a54927443f 100644 --- a/tests/ui/tag-type-args.rs +++ b/tests/ui/tag-type-args.rs @@ -1,4 +1,5 @@ enum Quux { Bar } +//~^ ERROR: parameter `T` is never used fn foo(c: Quux) { assert!((false)); } //~ ERROR missing generics for enum `Quux` diff --git a/tests/ui/tag-type-args.stderr b/tests/ui/tag-type-args.stderr index 49ecf65b7e62..80ffd3a2f050 100644 --- a/tests/ui/tag-type-args.stderr +++ b/tests/ui/tag-type-args.stderr @@ -1,5 +1,5 @@ error[E0107]: missing generics for enum `Quux` - --> $DIR/tag-type-args.rs:3:11 + --> $DIR/tag-type-args.rs:4:11 | LL | fn foo(c: Quux) { assert!((false)); } | ^^^^ expected 1 generic argument @@ -14,6 +14,16 @@ help: add missing generic argument LL | fn foo(c: Quux) { assert!((false)); } | +++ -error: aborting due to 1 previous error +error[E0392]: parameter `T` is never used + --> $DIR/tag-type-args.rs:1:11 + | +LL | enum Quux { Bar } + | ^ unused parameter + | + = 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: usize` instead -For more information about this error, try `rustc --explain E0107`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0107, E0392. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index d1b3cf71c15e..f6357bd9eb08 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -86,6 +86,8 @@ static A: () = (); //~^ ERROR attribute should be applied to a function impl Quux for u8 {} //~^ NOTE not a function +//~| NOTE missing `foo` in implementation +//~| ERROR missing: `foo` #[target_feature(enable = "sse2")] //~^ ERROR attribute should be applied to a function @@ -93,7 +95,7 @@ impl Foo {} //~^ NOTE not a function trait Quux { - fn foo(); + fn foo(); //~ NOTE `foo` from trait } impl Quux for Foo { diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index 29c73921c538..8f981d27c53f 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -117,7 +117,7 @@ LL | impl Quux for u8 {} | ------------------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:90:1 + --> $DIR/invalid-attribute.rs:92:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL | impl Foo {} | ----------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:108:5 + --> $DIR/invalid-attribute.rs:110:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,7 +138,7 @@ LL | | } | |_____- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:116:5 + --> $DIR/invalid-attribute.rs:118:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -183,7 +183,7 @@ LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/invalid-attribute.rs:100:5 + --> $DIR/invalid-attribute.rs:102:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -194,6 +194,16 @@ LL | fn foo() {} = note: see issue #69098 for more information = help: add `#![feature(target_feature_11)]` to the crate attributes to enable -error: aborting due to 22 previous errors +error[E0046]: not all trait items implemented, missing: `foo` + --> $DIR/invalid-attribute.rs:87:1 + | +LL | impl Quux for u8 {} + | ^^^^^^^^^^^^^^^^ missing `foo` in implementation +... +LL | fn foo(); + | --------- `foo` from trait -For more information about this error, try `rustc --explain E0658`. +error: aborting due to 23 previous errors + +Some errors have detailed explanations: E0046, E0658. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs index e0edd522431a..4cbc36f4650b 100644 --- a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs @@ -7,12 +7,14 @@ pub trait Trait { } impl Trait for i32 { + //~^ ERROR `S` is not constrained type Assoc = String; } // Should not not trigger suggestion here... impl Trait for () {} //~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied +//~| ERROR `S` is not constrained //... but should do so in all of the below cases except the last one fn func>(t: T) -> impl Trait<(), i32> { @@ -37,6 +39,7 @@ impl> Struct {} trait YetAnotherTrait {} impl, U> YetAnotherTrait for Struct {} //~^ ERROR struct takes 1 generic argument but 2 generic arguments were supplied +//~| ERROR `U` is not constrained fn main() { diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr index 711ccf1b6682..3c2b726fccea 100644 --- a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr @@ -1,5 +1,5 @@ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:14:12 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:15:12 | LL | impl Trait for () {} | ^^^^^ expected 1 generic argument @@ -11,7 +11,7 @@ LL | pub trait Trait { | ^^^^^ - error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:18:12 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:20:12 | LL | fn func>(t: T) -> impl Trait<(), i32> { | ^^^^^ expected 1 generic argument @@ -27,7 +27,7 @@ LL | fn func>(t: T) -> impl Trait<(), i32> { | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:18:46 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:20:46 | LL | fn func>(t: T) -> impl Trait<(), i32> { | ^^^^^ expected 1 generic argument @@ -43,7 +43,7 @@ LL | fn func>(t: T) -> impl Trait<(), Assoc = i32> { | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:24:18 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:26:18 | LL | struct Struct> { | ^^^^^ expected 1 generic argument @@ -59,7 +59,7 @@ LL | struct Struct> { | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:29:23 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:31:23 | LL | trait AnotherTrait> {} | ^^^^^ expected 1 generic argument @@ -75,7 +75,7 @@ LL | trait AnotherTrait> {} | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:32:9 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:34:9 | LL | impl> Struct {} | ^^^^^ expected 1 generic argument @@ -91,7 +91,7 @@ LL | impl> Struct {} | +++++++ error[E0107]: struct takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:38:58 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:40:58 | LL | impl, U> YetAnotherTrait for Struct {} | ^^^^^^ - help: remove this generic argument @@ -99,11 +99,30 @@ LL | impl, U> YetAnotherTrait for Struct {} | expected 1 generic argument | note: struct defined here, with 1 generic parameter: `T` - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:24:8 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:26:8 | LL | struct Struct> { | ^^^^^^ - -error: aborting due to 7 previous errors +error[E0207]: the type parameter `S` is not constrained by the impl trait, self type, or predicates + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:9:9 + | +LL | impl Trait for i32 { + | ^ unconstrained type parameter -For more information about this error, try `rustc --explain E0107`. +error[E0207]: the type parameter `S` is not constrained by the impl trait, self type, or predicates + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:15:9 + | +LL | impl Trait for () {} + | ^ unconstrained type parameter + +error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:40:35 + | +LL | impl, U> YetAnotherTrait for Struct {} + | ^ unconstrained type parameter + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0107, E0207. +For more information about an error, try `rustc --explain E0107`. 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 3d97bddb4a49..4c2e6f0852b6 100644 --- a/tests/ui/traits/bound/not-on-bare-trait-2021.rs +++ b/tests/ui/traits/bound/not-on-bare-trait-2021.rs @@ -7,10 +7,12 @@ trait Foo { fn foo(_x: Foo + Send) { //~^ ERROR trait objects must include the `dyn` keyword + //~| ERROR size for values of type } 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 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 6f41f872e4cf..57d3bc8f1094 100644 --- a/tests/ui/traits/bound/not-on-bare-trait-2021.stderr +++ b/tests/ui/traits/bound/not-on-bare-trait-2021.stderr @@ -1,3 +1,37 @@ +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:8 + | +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:8 + | +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 --> $DIR/not-on-bare-trait-2021.rs:8:12 | @@ -18,7 +52,7 @@ LL | fn foo(_x: &(dyn Foo + Send)) { | +++++ + error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/not-on-bare-trait-2021.rs:11:11 + --> $DIR/not-on-bare-trait-2021.rs:12:11 | LL | fn bar(x: Foo) -> Foo { | ^^^ @@ -37,7 +71,7 @@ LL | fn bar(x: &dyn Foo) -> Foo { | ++++ error[E0782]: trait objects must include the `dyn` keyword - --> $DIR/not-on-bare-trait-2021.rs:11:19 + --> $DIR/not-on-bare-trait-2021.rs:12:19 | LL | fn bar(x: Foo) -> Foo { | ^^^ @@ -51,6 +85,7 @@ help: alternatively, you can return an owned trait object LL | fn bar(x: Foo) -> Box { | +++++++ + -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0782`. +Some errors have detailed explanations: E0277, E0782. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/issue-106072.rs b/tests/ui/traits/issue-106072.rs index b174669545a2..d38d3c3b2865 100644 --- a/tests/ui/traits/issue-106072.rs +++ b/tests/ui/traits/issue-106072.rs @@ -1,4 +1,5 @@ #[derive(Clone)] //~ trait objects must include the `dyn` keyword +//~^ ERROR: the size for values of type `(dyn Foo + 'static)` cannot be known 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 1037603ceb77..aadadc45f21c 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:3:1 + --> $DIR/issue-106072.rs:4:1 | LL | struct Foo; | ----------- previous definition of the type `Foo` here @@ -8,6 +8,17 @@ 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[E0782]: trait objects must include the `dyn` keyword --> $DIR/issue-106072.rs:1:10 | @@ -16,7 +27,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 2 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0428, E0782. -For more information about an error, try `rustc --explain E0428`. +Some errors have detailed explanations: E0277, E0428, E0782. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/issue-28576.rs b/tests/ui/traits/issue-28576.rs index 972c839b6480..e19bd2635813 100644 --- a/tests/ui/traits/issue-28576.rs +++ b/tests/ui/traits/issue-28576.rs @@ -3,6 +3,8 @@ pub trait Foo { } pub trait Bar: Foo { + //~^ ERROR: the size for values of type `Self` cannot be known + //~| ERROR: the size for values of type `Self` cannot be known fn new(&self, b: & dyn Bar //~ ERROR the trait `Bar` cannot be made into an object diff --git a/tests/ui/traits/issue-28576.stderr b/tests/ui/traits/issue-28576.stderr index 3b45a510341f..96e8aaee23d5 100644 --- a/tests/ui/traits/issue-28576.stderr +++ b/tests/ui/traits/issue-28576.stderr @@ -1,5 +1,5 @@ error[E0038]: the trait `Bar` cannot be made into an object - --> $DIR/issue-28576.rs:7:12 + --> $DIR/issue-28576.rs:9:12 | LL | / dyn Bar LL | | @@ -19,6 +19,47 @@ help: consider using an opaque type instead LL | impl Bar | ~~~~ -error: aborting due to 1 previous error +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/issue-28576.rs:5:16 + | +LL | pub trait Bar: Foo { + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time + | +note: required by a bound in `Foo` + --> $DIR/issue-28576.rs:1:15 + | +LL | pub trait Foo { + | ^^^^^^^^ required by this bound in `Foo` +help: consider further restricting `Self` + | +LL | pub trait Bar: Foo + Sized { + | +++++++ +help: consider relaxing the implicit `Sized` restriction + | +LL | pub trait Foo { + | ++++++++ -For more information about this error, try `rustc --explain E0038`. +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/issue-28576.rs:5:16 + | +LL | pub trait Bar: Foo { + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time + | +note: required by a bound in `Foo` + --> $DIR/issue-28576.rs:1:15 + | +LL | pub trait Foo { + | ^^^^^^^^ required by this bound in `Foo` +help: consider further restricting `Self` + | +LL | ) where Self: Sized; + | +++++++++++++++++ +help: consider relaxing the implicit `Sized` restriction + | +LL | pub trait Foo { + | ++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0038, E0277. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/issue-38404.rs b/tests/ui/traits/issue-38404.rs index 05921b2c36e4..9b60116f733d 100644 --- a/tests/ui/traits/issue-38404.rs +++ b/tests/ui/traits/issue-38404.rs @@ -1,7 +1,8 @@ trait A: std::ops::Add + Sized {} trait B: A {} -trait C: A> {} +trait C: A> {} //~^ ERROR the trait `B` cannot be made into an object //~| ERROR the trait `B` cannot be made into an object +//~| ERROR the trait `B` cannot be made into an object fn main() {} diff --git a/tests/ui/traits/issue-38404.stderr b/tests/ui/traits/issue-38404.stderr index a5c258eb36e9..19d4035b54c8 100644 --- a/tests/ui/traits/issue-38404.stderr +++ b/tests/ui/traits/issue-38404.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `B` cannot be made into an object --> $DIR/issue-38404.rs:3:15 | -LL | trait C: A> {} - | ^^^^^^^^^^^^^^^^^^^^^^ `B` cannot be made into an object +LL | trait C: A> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ `B` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/issue-38404.rs:1:13 @@ -15,8 +15,8 @@ LL | trait B: A {} error[E0038]: the trait `B` cannot be made into an object --> $DIR/issue-38404.rs:3:15 | -LL | trait C: A> {} - | ^^^^^^^^^^^^^^^^^^^^^^ `B` cannot be made into an object +LL | trait C: A> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ `B` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/issue-38404.rs:1:13 @@ -27,6 +27,21 @@ LL | trait B: A {} | - this trait cannot be made into an object... = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error[E0038]: the trait `B` cannot be made into an object + --> $DIR/issue-38404.rs:3:15 + | +LL | trait C: A> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ `B` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/issue-38404.rs:1:13 + | +LL | trait A: std::ops::Add + Sized {} + | ^^^^^^^^^^^^^^^^^^^ ...because it uses `Self` as a type parameter +LL | trait B: A {} + | - this trait cannot be made into an object... + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/issue-87558.rs b/tests/ui/traits/issue-87558.rs index c5d86bd637b2..76f0f7453ddd 100644 --- a/tests/ui/traits/issue-87558.rs +++ b/tests/ui/traits/issue-87558.rs @@ -3,6 +3,8 @@ struct Error(ErrorKind); impl Fn(&isize) for Error { //~^ ERROR manual implementations of `Fn` are experimental //~| ERROR associated type bindings are not allowed here + //~| ERROR closure, found `Error` + //~| ERROR not all trait items implemented, missing: `call` fn from() {} //~ ERROR method `from` is not a member of trait `Fn` } diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr index b647f9794bd9..1ce273a9f25e 100644 --- a/tests/ui/traits/issue-87558.stderr +++ b/tests/ui/traits/issue-87558.stderr @@ -1,5 +1,5 @@ error[E0407]: method `from` is not a member of trait `Fn` - --> $DIR/issue-87558.rs:6:5 + --> $DIR/issue-87558.rs:8:5 | LL | fn from() {} | ^^^^^^^^^^^^ not a member of trait `Fn` @@ -24,7 +24,25 @@ help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>` LL | impl Fn(&isize) for Error { | ^^^^^^^^^^ -error: aborting due to 3 previous errors +error[E0277]: expected a `FnMut(&isize)` closure, found `Error` + --> $DIR/issue-87558.rs:3:21 + | +LL | impl Fn(&isize) for Error { + | ^^^^^ expected an `FnMut(&isize)` closure, found `Error` + | + = help: the trait `FnMut<(&isize,)>` is not implemented for `Error` +note: required by a bound in `Fn` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL -Some errors have detailed explanations: E0183, E0229, E0407. -For more information about an error, try `rustc --explain E0183`. +error[E0046]: not all trait items implemented, missing: `call` + --> $DIR/issue-87558.rs:3:1 + | +LL | impl Fn(&isize) for Error { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation + | + = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0046, E0183, E0229, E0277, E0407. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs index 64f09f823fc2..94733f88c2da 100644 --- a/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs +++ b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs @@ -6,6 +6,6 @@ fn foo() -> usize where for [i32; { let _: T = todo!(); 0 }]:, //~^ ERROR cannot capture late-bound type parameter in constant -{} +{ 42 } fn main() {} diff --git a/tests/ui/traits/object/object-unsafe-missing-assoc-type.rs b/tests/ui/traits/object/object-unsafe-missing-assoc-type.rs index 21f7fd92e80d..c83be544c0a1 100644 --- a/tests/ui/traits/object/object-unsafe-missing-assoc-type.rs +++ b/tests/ui/traits/object/object-unsafe-missing-assoc-type.rs @@ -3,5 +3,8 @@ trait Foo { } fn bar(x: &dyn Foo) {} //~ ERROR the trait `Foo` cannot be made into an object +//~^ ERROR the trait `Foo` cannot be made into an object +//~| ERROR the trait `Foo` cannot be made into an object +//~| ERROR the trait `Foo` cannot be made into an object fn main() {} diff --git a/tests/ui/traits/object/object-unsafe-missing-assoc-type.stderr b/tests/ui/traits/object/object-unsafe-missing-assoc-type.stderr index 196e74d39600..4c636c5e922e 100644 --- a/tests/ui/traits/object/object-unsafe-missing-assoc-type.stderr +++ b/tests/ui/traits/object/object-unsafe-missing-assoc-type.stderr @@ -13,6 +13,53 @@ LL | type Bar; | ^^^ ...because it contains the generic associated type `Bar` = help: consider moving `Bar` to another trait -error: aborting due to 1 previous error +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/object-unsafe-missing-assoc-type.rs:5:16 + | +LL | fn bar(x: &dyn Foo) {} + | ^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/object-unsafe-missing-assoc-type.rs:2:10 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | type Bar; + | ^^^ ...because it contains the generic associated type `Bar` + = help: consider moving `Bar` to another trait + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/object-unsafe-missing-assoc-type.rs:5:16 + | +LL | fn bar(x: &dyn Foo) {} + | ^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/object-unsafe-missing-assoc-type.rs:2:10 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | type Bar; + | ^^^ ...because it contains the generic associated type `Bar` + = help: consider moving `Bar` to another trait + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/object-unsafe-missing-assoc-type.rs:5:12 + | +LL | fn bar(x: &dyn Foo) {} + | ^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/object-unsafe-missing-assoc-type.rs:2:10 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | type Bar; + | ^^^ ...because it contains the generic associated type `Bar` + = help: consider moving `Bar` to another trait + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/transmutability/issue-101739-2.rs b/tests/ui/transmutability/issue-101739-2.rs index e5a56ccc9e03..4cde9152032c 100644 --- a/tests/ui/transmutability/issue-101739-2.rs +++ b/tests/ui/transmutability/issue-101739-2.rs @@ -16,6 +16,7 @@ mod assert { >() where Dst: BikeshedIntrinsicFrom< //~ ERROR trait takes at most 3 generic arguments but 6 generic arguments were supplied + //~^ ERROR: the constant `ASSUME_ALIGNMENT` is not of type `Assume` Src, Context, ASSUME_ALIGNMENT, diff --git a/tests/ui/transmutability/issue-101739-2.stderr b/tests/ui/transmutability/issue-101739-2.stderr index d5ed205d14b4..aed47f33f0d9 100644 --- a/tests/ui/transmutability/issue-101739-2.stderr +++ b/tests/ui/transmutability/issue-101739-2.stderr @@ -9,6 +9,22 @@ LL | | ASSUME_VALIDITY, LL | | ASSUME_VISIBILITY, | |_____________________________- help: remove these generic arguments -error: aborting due to 1 previous error +error: the constant `ASSUME_ALIGNMENT` is not of type `Assume` + --> $DIR/issue-101739-2.rs:18:14 + | +LL | Dst: BikeshedIntrinsicFrom< + | ______________^ +LL | | +LL | | Src, +LL | | Context, +... | +LL | | ASSUME_VISIBILITY, +LL | | >, + | |_________^ expected `Assume`, found `bool` + | +note: required by a bound in `BikeshedIntrinsicFrom` + --> $SRC_DIR/core/src/mem/transmutability.rs:LL:COL + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/type-alias-impl-trait/issue-77179.rs b/tests/ui/type-alias-impl-trait/issue-77179.rs index e7b04a489755..6e2ce76632fd 100644 --- a/tests/ui/type-alias-impl-trait/issue-77179.rs +++ b/tests/ui/type-alias-impl-trait/issue-77179.rs @@ -1,4 +1,4 @@ -// Regression test for #77179. +// Regression test for the ICE in #77179. #![feature(type_alias_impl_trait)] @@ -6,7 +6,9 @@ type Pointer = impl std::ops::Deref; fn test() -> Pointer<_> { //~^ ERROR: the placeholder `_` is not allowed within types + //~| ERROR: non-defining opaque type use in defining scope Box::new(1) + //~^ ERROR expected generic type parameter, found `i32` } fn main() { diff --git a/tests/ui/type-alias-impl-trait/issue-77179.stderr b/tests/ui/type-alias-impl-trait/issue-77179.stderr index 68dd6570d00c..c5b7c4178e47 100644 --- a/tests/ui/type-alias-impl-trait/issue-77179.stderr +++ b/tests/ui/type-alias-impl-trait/issue-77179.stderr @@ -7,6 +7,28 @@ LL | fn test() -> Pointer<_> { | | not allowed in type signatures | help: replace with the correct return type: `Pointer` -error: aborting due to 1 previous error +error[E0792]: non-defining opaque type use in defining scope + --> $DIR/issue-77179.rs:7:14 + | +LL | fn test() -> Pointer<_> { + | ^^^^^^^^^^ argument `i32` is not a generic parameter + | +note: for this opaque type + --> $DIR/issue-77179.rs:5:19 + | +LL | type Pointer = impl std::ops::Deref; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For more information about this error, try `rustc --explain E0121`. +error[E0792]: expected generic type parameter, found `i32` + --> $DIR/issue-77179.rs:10:5 + | +LL | type Pointer = impl std::ops::Deref; + | - this generic parameter must be used with a generic type parameter +... +LL | Box::new(1) + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0121, E0792. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/typeck/escaping_bound_vars.rs b/tests/ui/typeck/escaping_bound_vars.rs index 1fb063d2c268..f886388bfbd7 100644 --- a/tests/ui/typeck/escaping_bound_vars.rs +++ b/tests/ui/typeck/escaping_bound_vars.rs @@ -10,6 +10,10 @@ pub fn test() where (): Test<{ 1 + (<() as Elide(&())>::call) }>, //~^ ERROR cannot capture late-bound lifetime in constant + //~| ERROR associated type bindings are not allowed here + //~| ERROR the trait bound `(): Elide<(&(),)>` is not satisfied + //~| ERROR the trait bound `(): Elide<(&(),)>` is not satisfied + //~| ERROR cannot add { } diff --git a/tests/ui/typeck/escaping_bound_vars.stderr b/tests/ui/typeck/escaping_bound_vars.stderr index 3ea409435779..8c7dcdb7f161 100644 --- a/tests/ui/typeck/escaping_bound_vars.stderr +++ b/tests/ui/typeck/escaping_bound_vars.stderr @@ -6,5 +6,55 @@ LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, | | | lifetime defined here -error: aborting due to 1 previous error +error[E0229]: associated type bindings are not allowed here + --> $DIR/escaping_bound_vars.rs:11:28 + | +LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, + | ^^^^^^^^^^ associated type not allowed here +error[E0277]: the trait bound `(): Elide<(&(),)>` is not satisfied + --> $DIR/escaping_bound_vars.rs:11:22 + | +LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, + | ^^ the trait `Elide<(&(),)>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/escaping_bound_vars.rs:5:1 + | +LL | trait Elide { + | ^^^^^^^^^^^^^^ + +error[E0277]: cannot add `fn() {<() as Elide<(&(),)>>::call}` to `{integer}` + --> $DIR/escaping_bound_vars.rs:11:18 + | +LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, + | ^ no implementation for `{integer} + fn() {<() as Elide<(&(),)>>::call}` + | + = help: the trait `Add>::call}>` is not implemented for `{integer}` + = help: the following other types implement trait `Add`: + + > + + > + + > + + > + and 48 others + +error[E0277]: the trait bound `(): Elide<(&(),)>` is not satisfied + --> $DIR/escaping_bound_vars.rs:11:18 + | +LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, + | ^ the trait `Elide<(&(),)>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/escaping_bound_vars.rs:5:1 + | +LL | trait Elide { + | ^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0229, E0277. +For more information about an error, try `rustc --explain E0229`. diff --git a/tests/ui/typeck/issue-110052.rs b/tests/ui/typeck/issue-110052.rs index f124b58b5b60..acbfac30acb3 100644 --- a/tests/ui/typeck/issue-110052.rs +++ b/tests/ui/typeck/issue-110052.rs @@ -1,7 +1,7 @@ // Makes sure we deal with escaping lifetimes *above* INNERMOST when // suggesting trait for ambiguous associated type. -impl Validator for () +impl Validator for () where for<'iter> dyn Validator<<&'iter I>::Item>:, //~^ ERROR ambiguous associated type diff --git a/tests/ui/typeck/issue-79040.rs b/tests/ui/typeck/issue-79040.rs index 941612542207..03e008207566 100644 --- a/tests/ui/typeck/issue-79040.rs +++ b/tests/ui/typeck/issue-79040.rs @@ -1,5 +1,6 @@ fn main() { const FOO = "hello" + 1; //~ ERROR cannot add `{integer}` to `&str` //~^ missing type for `const` item + //~| ERROR cannot add `{integer}` to `&str` println!("{}", FOO); } diff --git a/tests/ui/typeck/issue-79040.stderr b/tests/ui/typeck/issue-79040.stderr index c820d1e08c4a..ce6a4b362170 100644 --- a/tests/ui/typeck/issue-79040.stderr +++ b/tests/ui/typeck/issue-79040.stderr @@ -12,6 +12,16 @@ error: missing type for `const` item LL | const FOO = "hello" + 1; | ^ help: provide a type for the item: `: ` -error: aborting due to 2 previous errors +error[E0369]: cannot add `{integer}` to `&str` + --> $DIR/issue-79040.rs:2:25 + | +LL | const FOO = "hello" + 1; + | ------- ^ - {integer} + | | + | &str + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/typeck/typeck-builtin-bound-type-parameters.rs b/tests/ui/typeck/typeck-builtin-bound-type-parameters.rs index e7e62c077394..5e5e88c8d0d6 100644 --- a/tests/ui/typeck/typeck-builtin-bound-type-parameters.rs +++ b/tests/ui/typeck/typeck-builtin-bound-type-parameters.rs @@ -4,11 +4,12 @@ fn foo1, U>(x: T) {} trait Trait: Copy {} //~^ ERROR trait takes 0 generic arguments but 1 generic argument was supplied //~| ERROR trait takes 0 generic arguments but 1 generic argument was supplied +//~| ERROR trait takes 0 generic arguments but 1 generic argument was supplied -struct MyStruct1>; +struct MyStruct1>(T); //~^ ERROR trait takes 0 generic arguments but 1 generic argument was supplied -struct MyStruct2<'a, T: Copy<'a>>; +struct MyStruct2<'a, T: Copy<'a>>(&'a T); //~^ ERROR trait takes 0 lifetime arguments but 1 lifetime argument was supplied fn foo2<'a, T:Copy<'a, U>, U>(x: T) {} diff --git a/tests/ui/typeck/typeck-builtin-bound-type-parameters.stderr b/tests/ui/typeck/typeck-builtin-bound-type-parameters.stderr index 67ca2b061f9b..1dc1d46aa71a 100644 --- a/tests/ui/typeck/typeck-builtin-bound-type-parameters.stderr +++ b/tests/ui/typeck/typeck-builtin-bound-type-parameters.stderr @@ -25,23 +25,23 @@ LL | trait Trait: Copy {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/typeck-builtin-bound-type-parameters.rs:8:21 + --> $DIR/typeck-builtin-bound-type-parameters.rs:9:21 | -LL | struct MyStruct1>; +LL | struct MyStruct1>(T); | ^^^^--- help: remove these generics | | | expected 0 generic arguments error[E0107]: trait takes 0 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/typeck-builtin-bound-type-parameters.rs:11:25 + --> $DIR/typeck-builtin-bound-type-parameters.rs:12:25 | -LL | struct MyStruct2<'a, T: Copy<'a>>; +LL | struct MyStruct2<'a, T: Copy<'a>>(&'a T); | ^^^^---- help: remove these generics | | | expected 0 lifetime arguments error[E0107]: trait takes 0 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/typeck-builtin-bound-type-parameters.rs:14:15 + --> $DIR/typeck-builtin-bound-type-parameters.rs:15:15 | LL | fn foo2<'a, T:Copy<'a, U>, U>(x: T) {} | ^^^^ -- help: remove this lifetime argument @@ -49,13 +49,23 @@ LL | fn foo2<'a, T:Copy<'a, U>, U>(x: T) {} | expected 0 lifetime arguments error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/typeck-builtin-bound-type-parameters.rs:14:15 + --> $DIR/typeck-builtin-bound-type-parameters.rs:15:15 | LL | fn foo2<'a, T:Copy<'a, U>, U>(x: T) {} | ^^^^ - help: remove this generic argument | | | expected 0 generic arguments -error: aborting due to 7 previous errors +error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/typeck-builtin-bound-type-parameters.rs:4:14 + | +LL | trait Trait: Copy {} + | ^^^^---------- help: remove these generics + | | + | expected 0 generic arguments + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/typeck/typeck_type_placeholder_item.rs b/tests/ui/typeck/typeck_type_placeholder_item.rs index 4eba14f5a93f..f6d1fb6a23d4 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item.rs @@ -198,6 +198,7 @@ trait Qux { //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated types } impl Qux for Struct { + //~^ ERROR: not all trait items implemented, missing: `F` type A = _; //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated types type B = _; diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index a4325b01f02c..bfcc76c1dae7 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -29,7 +29,7 @@ LL | struct BadStruct2<_, T>(_, T); | ^ expected identifier, found reserved identifier error: associated constant in `impl` without body - --> $DIR/typeck_type_placeholder_item.rs:205:5 + --> $DIR/typeck_type_placeholder_item.rs:206:5 | LL | const C: _; | ^^^^^^^^^^- @@ -411,7 +411,7 @@ LL | type Y = impl Trait<_>; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/typeck_type_placeholder_item.rs:216:31 + --> $DIR/typeck_type_placeholder_item.rs:217:31 | LL | fn value() -> Option<&'static _> { | ----------------^- @@ -420,7 +420,7 @@ LL | fn value() -> Option<&'static _> { | help: replace with the correct return type: `Option<&'static u8>` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:221:10 + --> $DIR/typeck_type_placeholder_item.rs:222:10 | LL | const _: Option<_> = map(value); | ^^^^^^^^^ @@ -429,7 +429,7 @@ LL | const _: Option<_> = map(value); | help: replace with the correct type: `Option` error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/typeck_type_placeholder_item.rs:224:31 + --> $DIR/typeck_type_placeholder_item.rs:225:31 | LL | fn evens_squared(n: usize) -> _ { | ^ @@ -438,13 +438,13 @@ LL | fn evens_squared(n: usize) -> _ { | help: replace with an appropriate return type: `impl Iterator` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:229:10 + --> $DIR/typeck_type_placeholder_item.rs:230:10 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^ not allowed in type signatures | -note: however, the inferred type `Map, {closure@typeck_type_placeholder_item.rs:229:29}>, {closure@typeck_type_placeholder_item.rs:229:49}>` cannot be named - --> $DIR/typeck_type_placeholder_item.rs:229:14 +note: however, the inferred type `Map, {closure@typeck_type_placeholder_item.rs:230:29}>, {closure@typeck_type_placeholder_item.rs:230:49}>` cannot be named + --> $DIR/typeck_type_placeholder_item.rs:230:14 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -631,25 +631,25 @@ LL | fn clone_from(&mut self, other: &FnTest9) { *self = FnTest9; } | ~~~~~~~~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:201:14 + --> $DIR/typeck_type_placeholder_item.rs:202:14 | LL | type A = _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:203:14 + --> $DIR/typeck_type_placeholder_item.rs:204:14 | LL | type B = _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:205:14 + --> $DIR/typeck_type_placeholder_item.rs:206:14 | LL | const C: _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:208:14 + --> $DIR/typeck_type_placeholder_item.rs:209:14 | LL | const D: _ = 42; | ^ @@ -657,7 +657,16 @@ LL | const D: _ = 42; | not allowed in type signatures | help: replace with the correct type: `i32` -error: aborting due to 71 previous errors +error[E0046]: not all trait items implemented, missing: `F` + --> $DIR/typeck_type_placeholder_item.rs:200:1 + | +LL | type F: std::ops::Fn(_); + | ----------------------- `F` from trait +... +LL | impl Qux for Struct { + | ^^^^^^^^^^^^^^^^^^^ missing `F` in implementation -Some errors have detailed explanations: E0121, E0282, E0403. -For more information about an error, try `rustc --explain E0121`. +error: aborting due to 72 previous errors + +Some errors have detailed explanations: E0046, E0121, E0282, E0403. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/typeck/typeck_type_placeholder_item_help.rs b/tests/ui/typeck/typeck_type_placeholder_item_help.rs index 914f8a2b28b3..ff6182588c72 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item_help.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item_help.rs @@ -27,7 +27,7 @@ impl Test6 { } pub fn main() { - let _: Option = test1(); - let _: f64 = test1(); + let _: Option = test1(); //~ ERROR mismatched types + let _: f64 = test1(); //~ ERROR mismatched types let _: Option = test1(); } diff --git a/tests/ui/typeck/typeck_type_placeholder_item_help.stderr b/tests/ui/typeck/typeck_type_placeholder_item_help.stderr index ed6f4088019f..b0d4ed8272be 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item_help.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item_help.stderr @@ -55,6 +55,29 @@ LL | const TEST6: _ = 13; | not allowed in type signatures | help: replace with the correct type: `i32` -error: aborting due to 7 previous errors +error[E0308]: mismatched types + --> $DIR/typeck_type_placeholder_item_help.rs:30:28 + | +LL | let _: Option = test1(); + | ------------- ^^^^^^^ expected `Option`, found `Option` + | | + | expected due to this + | + = note: expected enum `Option` + found enum `Option` -For more information about this error, try `rustc --explain E0121`. +error[E0308]: mismatched types + --> $DIR/typeck_type_placeholder_item_help.rs:31:18 + | +LL | let _: f64 = test1(); + | --- ^^^^^^^ expected `f64`, found `Option` + | | + | expected due to this + | + = note: expected type `f64` + found enum `Option` + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0121, E0308. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/unboxed-closures/unboxed-closure-sugar-region.rs b/tests/ui/unboxed-closures/unboxed-closure-sugar-region.rs index c575f507704e..ea73b8b3c4a2 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-sugar-region.rs +++ b/tests/ui/unboxed-closures/unboxed-closure-sugar-region.rs @@ -21,10 +21,12 @@ fn same_type>(a: A, b: B) { } fn test<'a,'b>() { // Parens are equivalent to omitting default in angle. eq::< dyn Foo<(isize,),Output=()>, dyn Foo(isize) >(); + //~^ ERROR trait takes 1 lifetime argument but 0 lifetime arguments were supplied // Here we specify 'static explicitly in angle-bracket version. // Parenthesized winds up getting inferred. eq::< dyn Foo<'static, (isize,),Output=()>, dyn Foo(isize) >(); + //~^ ERROR trait takes 1 lifetime argument but 0 lifetime arguments were supplied } fn test2(x: &dyn Foo<(isize,),Output=()>, y: &dyn Foo(isize)) { diff --git a/tests/ui/unboxed-closures/unboxed-closure-sugar-region.stderr b/tests/ui/unboxed-closures/unboxed-closure-sugar-region.stderr index 0465c20dffa2..d73aef851fd7 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-sugar-region.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-sugar-region.stderr @@ -1,5 +1,5 @@ error[E0107]: trait takes 1 lifetime argument but 0 lifetime arguments were supplied - --> $DIR/unboxed-closure-sugar-region.rs:30:51 + --> $DIR/unboxed-closure-sugar-region.rs:32:51 | LL | fn test2(x: &dyn Foo<(isize,),Output=()>, y: &dyn Foo(isize)) { | ^^^ expected 1 lifetime argument @@ -10,6 +10,30 @@ note: trait defined here, with 1 lifetime parameter: `'a` LL | trait Foo<'a,T> { | ^^^ -- -error: aborting due to 1 previous error +error[E0107]: trait takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/unboxed-closure-sugar-region.rs:23:58 + | +LL | eq::< dyn Foo<(isize,),Output=()>, dyn Foo(isize) >(); + | ^^^ expected 1 lifetime argument + | +note: trait defined here, with 1 lifetime parameter: `'a` + --> $DIR/unboxed-closure-sugar-region.rs:10:7 + | +LL | trait Foo<'a,T> { + | ^^^ -- + +error[E0107]: trait takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/unboxed-closure-sugar-region.rs:28:58 + | +LL | eq::< dyn Foo<'static, (isize,),Output=()>, dyn Foo(isize) >(); + | ^^^ expected 1 lifetime argument + | +note: trait defined here, with 1 lifetime parameter: `'a` + --> $DIR/unboxed-closure-sugar-region.rs:10:7 + | +LL | trait Foo<'a,T> { + | ^^^ -- + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.rs b/tests/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.rs index 2c7e12f32575..43db7870ada5 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.rs +++ b/tests/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.rs @@ -2,25 +2,25 @@ trait Zero { fn dummy(&self); } -fn foo1(_: dyn Zero()) { +fn foo1(_: &dyn Zero()) { //~^ ERROR trait takes 0 generic arguments but 1 generic argument //~| ERROR associated type `Output` not found for `Zero` } -fn foo2(_: dyn Zero) { +fn foo2(_: &dyn Zero) { //~^ ERROR trait takes 0 generic arguments but 1 generic argument } -fn foo3(_: dyn Zero < usize >) { +fn foo3(_: &dyn Zero < usize >) { //~^ ERROR trait takes 0 generic arguments but 1 generic argument } -fn foo4(_: dyn Zero(usize)) { +fn foo4(_: &dyn Zero(usize)) { //~^ ERROR trait takes 0 generic arguments but 1 generic argument //~| ERROR associated type `Output` not found for `Zero` } -fn foo5(_: dyn Zero ( usize )) { +fn foo5(_: &dyn Zero ( usize )) { //~^ ERROR trait takes 0 generic arguments but 1 generic argument //~| ERROR associated type `Output` not found for `Zero` } diff --git a/tests/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.stderr b/tests/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.stderr index 50b90553aa71..5a2de132d70e 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.stderr @@ -1,10 +1,10 @@ error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:5:16 + --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:5:17 | -LL | fn foo1(_: dyn Zero()) { - | ^^^^-- help: remove these parenthetical generics - | | - | expected 0 generic arguments +LL | fn foo1(_: &dyn Zero()) { + | ^^^^-- help: remove these parenthetical generics + | | + | expected 0 generic arguments | note: trait defined here, with 0 generic parameters --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7 @@ -13,18 +13,18 @@ LL | trait Zero { fn dummy(&self); } | ^^^^ error[E0220]: associated type `Output` not found for `Zero` - --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:5:16 + --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:5:17 | -LL | fn foo1(_: dyn Zero()) { - | ^^^^^^ associated type `Output` not found +LL | fn foo1(_: &dyn Zero()) { + | ^^^^^^ associated type `Output` not found error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:10:16 + --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:10:17 | -LL | fn foo2(_: dyn Zero) { - | ^^^^------- help: remove these generics - | | - | expected 0 generic arguments +LL | fn foo2(_: &dyn Zero) { + | ^^^^------- help: remove these generics + | | + | expected 0 generic arguments | note: trait defined here, with 0 generic parameters --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7 @@ -33,12 +33,12 @@ LL | trait Zero { fn dummy(&self); } | ^^^^ error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:14:16 + --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:14:17 | -LL | fn foo3(_: dyn Zero < usize >) { - | ^^^^-------------- help: remove these generics - | | - | expected 0 generic arguments +LL | fn foo3(_: &dyn Zero < usize >) { + | ^^^^-------------- help: remove these generics + | | + | expected 0 generic arguments | note: trait defined here, with 0 generic parameters --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7 @@ -47,12 +47,12 @@ LL | trait Zero { fn dummy(&self); } | ^^^^ error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:18:16 + --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:18:17 | -LL | fn foo4(_: dyn Zero(usize)) { - | ^^^^------- help: remove these parenthetical generics - | | - | expected 0 generic arguments +LL | fn foo4(_: &dyn Zero(usize)) { + | ^^^^------- help: remove these parenthetical generics + | | + | expected 0 generic arguments | note: trait defined here, with 0 generic parameters --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7 @@ -61,18 +61,18 @@ LL | trait Zero { fn dummy(&self); } | ^^^^ error[E0220]: associated type `Output` not found for `Zero` - --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:18:16 + --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:18:17 | -LL | fn foo4(_: dyn Zero(usize)) { - | ^^^^^^^^^^^ associated type `Output` not found +LL | fn foo4(_: &dyn Zero(usize)) { + | ^^^^^^^^^^^ associated type `Output` not found error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:23:16 + --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:23:17 | -LL | fn foo5(_: dyn Zero ( usize )) { - | ^^^^-------------- help: remove these parenthetical generics - | | - | expected 0 generic arguments +LL | fn foo5(_: &dyn Zero ( usize )) { + | ^^^^-------------- help: remove these parenthetical generics + | | + | expected 0 generic arguments | note: trait defined here, with 0 generic parameters --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7 @@ -81,10 +81,10 @@ LL | trait Zero { fn dummy(&self); } | ^^^^ error[E0220]: associated type `Output` not found for `Zero` - --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:23:16 + --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:23:17 | -LL | fn foo5(_: dyn Zero ( usize )) { - | ^^^^^^^^^^^^^^^^^^ associated type `Output` not found +LL | fn foo5(_: &dyn Zero ( usize )) { + | ^^^^^^^^^^^^^^^^^^ associated type `Output` not found error: aborting due to 8 previous errors From fa5bef849e1224b4f086ca70931ea811380e1943 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 9 Jan 2024 22:00:03 +0100 Subject: [PATCH 254/257] rint intrinsics: caution against actually trying to check for floating-point exceptions --- library/core/src/intrinsics.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 3d5b544bc1bc..3df3e8ea05cd 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1787,6 +1787,8 @@ extern "rust-intrinsic" { /// so this rounds half-way cases to the number with an even least significant digit. /// /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so this is not something that + /// can actually be used from Rust code. /// /// The stabilized version of this intrinsic is /// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even) @@ -1796,6 +1798,8 @@ extern "rust-intrinsic" { /// so this rounds half-way cases to the number with an even least significant digit. /// /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so this is not something that + /// can actually be used from Rust code. /// /// The stabilized version of this intrinsic is /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) From 5b4b7b7cf86f7f30a6a394ed6e8ebaf89eeebc14 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 9 Jan 2024 22:32:55 -0500 Subject: [PATCH 255/257] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 2ce45605d9db..3e428a38a34e 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 2ce45605d9db521b5fd6c1211ce8de6055fdb24e +Subproject commit 3e428a38a34e820a461d2cc082e726d3bda71bcb From 7a75766c41893b39352171dd313557f79f2b9f9d Mon Sep 17 00:00:00 2001 From: The Miri Conjob Bot Date: Wed, 10 Jan 2024 05:03:16 +0000 Subject: [PATCH 256/257] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 548e12ffdeaf..bdf2416a20e6 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -5bcd86d89b2b7b6a490f7e075dd4eb346deb5f98 +94807670a6a3834cc9b71b0b803d49d307c9ba5d From 075f2e0345a00fcb3352ac9ece8a87817b73d305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emil=20Gardstr=C3=B6m?= Date: Wed, 10 Jan 2024 10:20:26 +0100 Subject: [PATCH 257/257] Add `#[track_caller]` to the "From implies Into" impl --- library/core/src/convert/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 8c01b0973d65..45f6e375e894 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -753,6 +753,7 @@ where /// That is, this conversion is whatever the implementation of /// [From]<T> for U chooses to do. #[inline] + #[track_caller] fn into(self) -> U { U::from(self) }